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;
}