bzoj 2756: [SCOI2012]奇怪的游戏
Description
Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。
Input
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
Output
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
Sample Input
2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
2
-1
-1
HINT
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
Source
一道很不错的网络流啊,一开始以为是所有数都变成最大的那个数,然后以为是个大水题,wa了之后觉得可以二分,结果又wa了;
看了题解之后发现自己简直mdzz;
首先看到这种题目的第一反应就是黑白染色,然后我们设黑点的数量为num1,黑点的权值和为sum1,白点的数量为num2,白点的权值和为sum2;
那么我们假设变为的数=x,那么num1*x-sum1=num2*x-sum2,如果num1!=num2,那么x可以直接解出来,x=(sum1-sum2)/(num1-num2);
如果num1=num2,因为每个点都能有匹配,那么答案满足可二分性,因为假如能变成x,那么对于k>=x,那么肯定也能变成k,因为可以先变成x,然后一对匹配就一直加;
所以对于这个num1=num2二分x就可以了;
把x求出来之后用最大流check:
//MADE BY QT666 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define int long long using namespace std; typedef long long ll; const int N=100050; const int Inf=3e15; int head[N],to[N],nxt[N],s[N],q[N],cnt=1,level[N],S,T; ll F; void Addedge(int x,int y,int z) { to[++cnt]=y,s[cnt]=z,nxt[cnt]=head[x],head[x]=cnt; } void lnk(int x,int y,int z){ Addedge(x,y,z),Addedge(y,x,0); } bool bfs(){ for(int i=S;i<=T;i++) level[i]=0; q[0]=S,level[S]=1;int t=0,sum=1; while(t<sum){ int x=q[t++]; if(x==T) return 1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(s[i]&&level[y]==0){ level[y]=level[x]+1; q[sum++]=y; } } } return 0; } int dfs(int x,int maxf){ if(x==T) return maxf; int ret=0; for(int i=head[x];i;i=nxt[i]){ int y=to[i];int f=s[i]; if(level[y]==level[x]+1&&f){ int minn=min(f,maxf-ret); f=dfs(y,minn); s[i]-=f,s[i^1]+=f,ret+=f; if(ret==maxf) break; } } if(!ret) level[x]=0; return ret; } void Dinic(){ F=0;while(bfs()) F+=dfs(S,Inf); } bool check(){ for(int i=head[S];i;i=nxt[i]) if(s[i]) return 0; for(int i=head[T];i;i=nxt[i]) if(s[i^1]) return 0; return 1; } int a[100][100],maxn,xh[100][100]; int mx[]={1,-1,0,0},my[]={0,0,1,-1},n,m,tt,tot; bool judge(int mid){ memset(head,0,sizeof(head));cnt=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if((i+j)%2==0){ lnk(S,xh[i][j],mid-a[i][j]); for(int k=0;k<4;k++){ int x=i+mx[k],y=j+my[k]; if(x>=1&&x<=n&&y>=1&&y<=m) lnk(xh[i][j],xh[x][y]+tt,Inf); } } else lnk(xh[i][j]+tt,T,mid-a[i][j]); } } Dinic();return check(); } main(){ int t;scanf("%lld",&t); while(t--){ scanf("%lld%lld",&n,&m);maxn=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%lld",&a[i][j]);maxn=max(maxn,a[i][j]); } } tt=0,tot=0;int sum1=0,sum2=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if((i+j)%2==0) xh[i][j]=++tt,sum1+=a[i][j]; else xh[i][j]=++tot,sum2+=a[i][j]; } } T=tt+tot+1; if(tt!=tot){ int mid=(sum1-sum2)/(tt-tot); if(judge(mid)) cout<<F<<endl; else puts("-1"); } if(tt==tot){ int l=maxn,r=2e9,ans=-1; while(l<=r){ int mid=(l+r)>>1; if(judge(mid)) ans=F,r=mid-1; else l=mid+1; } cout<<ans<<endl; } } return 0; }