SCOI2012 奇怪的游戏

题目链接:戳我

怎么说呢,看到棋盘(应该想到二分图染色)

设白色格子数量为\(cnt0\),现在的值的和为\(sum0\)。黑色格子的数量为\(cnt1\),现在的值的和为\(sum1\),最后的答案为x。

\(sum0+cnt0*x=sum1+cnt1*x\)
\(sum0-sum1=x*(cnt1-cnt0)\)

如果\(cnt1-cnt0\)不为0的时候,我们可以直接获得答案,然后验证答案的正确性。

我们不容易知道到底最后的终止数为多少,但是我们知道如果格子数量是偶数的话,如果一个x成立,那么比它大的数一定也成立,但是比它小的数就不一定成立了。所以可!以!二!分!,然后把这个题转化成一个判定性问题。但是最后也别忘了验证答案的正确性。(二分的范围取$[l,l+0x3f3f3f3f]就行了)

怎么验证答案的正确性呢?不就是是否存在一个方案使得按照题目的意思进行操作,所有的数都能达到x吗。

我们既然黑白染色了,所以就可以将整个棋盘转化成一张二分图。

如果还需要增加多少,就向S或者T连容量为多少的边。然后相邻的格子连INF。

最后注意因为这个数比较大,所以要开long long!!!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<ctime>
#define S 0
#define T n*m+1
#define MAXN 2010
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,m,t=1,tt;
int a[45][45];
int head[MAXN],dep[MAXN],cur[MAXN],dis[MAXN];
int move_x[4]={1,-1,0,0},move_y[4]={0,0,1,-1};
struct Edge{int nxt,to;long long dis;}edge[2000010];
inline void add(int from,int to,long long dis)
{
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool bfs()
{
    memset(dep,0x3f,sizeof(dep));
    memcpy(cur,head,sizeof(head));
    queue<int>q;
    q.push(S);
    dep[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dep[v]==0x3f3f3f3f&&edge[i].dis)
            {
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    if(dep[T]==0x3f3f3f3f) return false;
    return true;
}
inline long long dfs(int x,long long f)
{
    if(x==T||!f) return f;
    long long used=0,w;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        cur[x]=i;
        if(dep[edge[i].to]==dep[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
        {
            used+=w,f-=w;
            edge[i].dis-=w,edge[i^1].dis+=w;
            if(!f) break;
        }
    }
    return used;
}
inline long long dinic()
{
    long long cur_ans=0;
    while(bfs()) cur_ans+=dfs(S,INF);
    return cur_ans;
}
inline int id(int x,int y){return (x-1)*m+y;}
inline bool check(long long x)
{
    long long all=0;
    memset(head,0,sizeof(head));
    t=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if((i+j)&1) 
            {
                add(S,id(i,j),x-a[i][j]);
                for(int k=0;k<=3;k++)
                {
                    int xx=i+move_x[k];
                    int yy=j+move_y[k];
                    if(xx<1||xx>n||yy<1||yy>m) continue;
                    add(id(i,j),id(xx,yy),INF);
                }
                all+=x-a[i][j];
            }
            else add(id(i,j),T,x-a[i][j]);
        }
    long long cur_ans=dinic();
    if(cur_ans!=all) return false;
    return true;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&tt);
    while(tt--)
    {
        memset(head,0,sizeof(head));
        t=1;
        long long tot=0;
        scanf("%d%d",&n,&m);
        long long l=0,r=l+INF;
        long long sum0=0,sum1=0,cnt0=0,cnt1=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]),l=max(l,1ll*a[i][j]);
                tot+=a[i][j];
                if((i+j)&1) cnt0++,sum0+=a[i][j];
                else cnt1++,sum1+=a[i][j];
            }
        if(cnt0!=cnt1)
        {
            long long ans=(sum0-sum1)/(cnt0-cnt1);
            if(check(ans)==true&&1ll*ans>=l) printf("%lld\n",(1ll*ans*n*m-tot)/2);
            else printf("-1\n");
        }
        else
        {
            if(sum0!=sum1) {printf("-1\n");continue;}
            long long ans=-1;
            while(l<=r)
            {
                long long mid=(l+r)>>1;
                if(check(mid)) ans=mid,r=mid-1;
                else l=mid+1;
            }
            if(ans==-1) printf("-1\n");
            else printf("%lld\n",(1ll*ans*n*m-tot)/2);
        }
    }
    
    return 0;
}
posted @ 2019-05-19 10:32  风浔凌  阅读(147)  评论(0编辑  收藏  举报