其他分享
首页 > 其他分享> > 洛谷P5038 [SCOI2012]奇怪的游戏

洛谷P5038 [SCOI2012]奇怪的游戏

作者:互联网

题目大意:

有一个\(n*m\)的网格,每个格子上有一个数字,每次选两个格子将上面的数字\(+1\),问能不能使网格上所有数字变成相同的,输出操作次数

网格图大部分都黑白染色了吧(

然后我们设黑色格子个数为\(B\),权值和为\(b\),白色格子个数为\(W\),权值和为\(w\)

然而每次操作必定黑色和白色格子各\(+1\),所以\(b\ne w\)的话直接无解

当\(B\ne W\)时:

假设最后数字都变成\(X\)

\[B*X-b=W*X-w\]

化简

\[X=\frac{b-w}{B-W}\]

可以直接解出\(X\),当然\(X\)不一定可取,需要\(check\)一波

当\(B=W\)时,我们可以通过覆盖整个棋盘让所有数字\(+1\),所以答案有单调性

那么就可以二分然后\(check\)

\(check\)怎么写呢?

对于每个黑色节点,我们由超级源点向他连\(X-val[i][j]\)的边,向相邻节点连\(inf\)边

对于每个白色节点,向超级汇点连\(X-val[i][j]\)边,和为\(sum\)

然后看最大流是否等于\(sum\)

其实是很套路的棋盘问题的最大流判断

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
    int x=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=0,ch=getchar();
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
const int inf=0x3f3f3f3f3f3f3f3f;
int tyx;
int n,m,sum1,sum2,val1,val2,maxn,ret;
int st,ed,tot;
int val[50][50],id[50][50];
int head[100010],cur[100010],d[100010],cnt=1;
struct point
{
    int nxt,to,c;
}a[200010];
inline void add(int x,int y,int c)
{
    a[++cnt]=(point){head[x],y,c};head[x]=cnt;
    a[++cnt]=(point){head[y],x,0};head[y]=cnt;
}
queue<int> q;
inline bool bfs()
{
    for(int i=1;i<=ed;++i) cur[i]=head[i],d[i]=0;
    q.push(st);d[st]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=head[now];i;i=a[i].nxt)
        {
            int t=a[i].to;
            if(!d[t]&&a[i].c)
            {
                d[t]=d[now]+1;
                q.push(t);
            }
        }
    }
    return d[ed];
}
inline int dfs(int now,int c)
{
    if(now==ed||!c) return c;
    int ret=c,f;
    for(int i=cur[now];i;i=a[i].nxt)
    {
        cur[now]=i;
        int t=a[i].to;
        if(d[t]==d[now]+1)
        {
            f=dfs(t,min(ret,a[i].c));
            if(!f) continue;
            a[i].c-=f;
            a[i^1].c+=f;
            ret-=f;
            if(!ret) return c;
        }
    }
    if(ret==c) d[now]=0;
    return c-ret;
}
inline int dinic()
{
    int ret=0;
    while(bfs()) ret+=dfs(st,inf);
    return ret;
}
inline bool check(int x)
{
    memset(head,0,sizeof(head));cnt=1;
    int sum=0;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            if((i+j)&1)
            {
                sum+=x-val[i][j];
                add(st,id[i][j],x-val[i][j]);
                for(int k=0;k<4;++k)
                {
                    int tx=i+dx[k],ty=j+dy[k];
                    if(tx<1||ty<1||tx>n||ty>m) continue;
                    add(id[i][j],id[tx][ty],inf);
                }
            }
            else add(id[i][j],ed,x-val[i][j]);
        }
    }
    return dinic()==sum;
}
signed main()
{
    tyx=read();
    while(tyx--)
    {
        sum1=sum2=val1=val2=maxn=0;
        n=read(),m=read();
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                val[i][j]=read();
                id[i][j]=++tot;
                maxn=max(maxn,val[i][j]);
                if((i+j)&1) ++sum1,val1+=val[i][j];
                else ++sum2,val2+=val[i][j];
            }
        }
        st=++tot,ed=++tot;
        if(sum1!=sum2)//不相等时 B*t-b==W*t-w
        {             //(B-W)*t=b-w,t=(b-w)/(B-W)
            int t=(val1-val2)/(sum1-sum2);
            if(t>=maxn&&check(t)) printf("%lld\n",t*sum2-val2);
            else puts("-1");
            continue;
        }
        if(val1!=val2) {puts("-1");continue;}//黑白相等二分答案,总数和不相等无解
            int l=maxn,r=1e15;ret=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) ret=mid,r=mid-1;
            else l=mid+1;
        }
        if(!ret) puts("-1");
        else printf("%lld\n",ret*sum2-val2);
    }
return 0;
}

标签:ch,洛谷,int,mid,ret,SCOI2012,read,P5038,check
来源: https://www.cnblogs.com/knife-rose/p/12099151.html