bzoj2756 [SCOI2012]奇怪的游戏
Description
Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1。
Input
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
Output
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
Sample Input
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
-1
HINT
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
正解:二分+最大流。
这道题还是很不容易想啊,不过我写了个骗分居然有$90$分。。
首先我们肯定是要黑白染色,相邻的点染不同的颜色。
然后我们按照格子数分奇偶讨论:
如果格子数为偶数,那么黑点和白点个数相同,于是所有黑点的和与所有白点的和必须相同,否则一定不合法。
因为当黑点和白点个数相同时,每个点的权值$+1$,等价于将相邻的上下两点匹配一次,所以我们发现每个点的最终权值是满足单调性的。也就是说如果$v$合法,那么$v+1$也一定合法,于是我们直接二分最终的权值,建图跑最大流就行了。
当格子数为奇数时,黑点与白点个数不同。那么我们可以直接算出最终每个格子的权值。
由$v*num_{black}-sum_{black}=v*num_{white}-sum_{white}$,解得$v=(sum_{black}-sum_{while})/(num_{black}-num_{white})$。
$num$为格子数量,$sum$为初始的格子权值和。
那么我们判断一下$v$是否合法就行了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define inf (1LL<<60) 6 #define N (1000010) 7 #define pos(i,j) ((i-1)*m+j) 8 9 using namespace std; 10 11 struct edge{ int nt,to; ll flow,cap; }g[N<<1]; 12 13 int head[N],d[N],q[N],a[42][42],S,T,n,m,num,tot; 14 ll x,y,ans; 15 16 il int gi(){ 17 RG int x=0,q=1; RG char ch=getchar(); 18 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 19 if (ch=='-') q=-1,ch=getchar(); 20 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 21 return q*x; 22 } 23 24 il void insert(RG int from,RG int to,RG ll cap){ 25 g[++num]=(edge){head[from],to,0,cap},head[from]=num; return; 26 } 27 28 il int bfs(RG int S,RG int T){ 29 for (RG int i=1;i<=T;++i) d[i]=0; 30 RG int h=0,t=1; q[t]=S,d[S]=1; 31 while (h<t){ 32 RG int x=q[++h],v; 33 for (RG int i=head[x];i;i=g[i].nt){ 34 v=g[i].to; 35 if (!d[v] && g[i].cap>g[i].flow){ 36 d[v]=d[x]+1,q[++t]=v; 37 if (v==T) return 1; 38 } 39 } 40 } 41 return d[T]; 42 } 43 44 il ll dfs(RG int x,RG int T,RG ll a){ 45 if (!a || x==T) return a; RG ll flow=0,f; RG int v; 46 for (RG int i=head[x];i;i=g[i].nt){ 47 v=g[i].to; 48 if (d[v]==d[x]+1 && g[i].cap>g[i].flow){ 49 f=dfs(v,T,min(a,g[i].cap-g[i].flow)); 50 if (!f){ d[v]=0; continue; } 51 g[i].flow+=f,g[i^1].flow-=f; 52 flow+=f,a-=f; if (!a) return flow; 53 } 54 } 55 return flow; 56 } 57 58 il ll maxflow(RG int S,RG int T){ 59 RG ll flow=0; 60 while (bfs(S,T)) flow+=dfs(S,T,inf); 61 return flow; 62 } 63 64 il int check(RG ll key){ 65 for (RG int i=1;i<=T;++i) head[i]=0; num=1; 66 for (RG int i=1;i<=n;++i) 67 for (RG int j=1;j<=m;++j){ 68 if ((i+j)&1) insert(S,pos(i,j),key-a[i][j]),insert(pos(i,j),S,0); 69 else insert(pos(i,j),T,key-a[i][j]),insert(T,pos(i,j),0); 70 } 71 for (RG int i=1;i<=n;++i) 72 for (RG int j=1;j<=m;++j) 73 if ((i+j)&1){ 74 if (i>1) insert(pos(i,j),pos(i-1,j),inf),insert(pos(i-1,j),pos(i,j),0); 75 if (i<n) insert(pos(i,j),pos(i+1,j),inf),insert(pos(i+1,j),pos(i,j),0); 76 if (j>1) insert(pos(i,j),pos(i,j-1),inf),insert(pos(i,j-1),pos(i,j),0); 77 if (j<m) insert(pos(i,j),pos(i,j+1),inf),insert(pos(i,j+1),pos(i,j),0); 78 } 79 maxflow(S,T); 80 for (RG int i=1,k=2;i<=n;++i) 81 for (RG int j=1;j<=m;++j,k+=2) 82 if (g[k].flow!=g[k].cap) return 0; 83 return 1; 84 } 85 86 il void work(){ 87 n=gi(),m=gi(),tot=x=y=0,S=n*m+1,T=S+1; 88 for (RG int i=1;i<=n;++i) 89 for (RG int j=1;j<=m;++j){ 90 a[i][j]=gi(),tot=max(tot,a[i][j]); 91 if ((i+j)&1) x+=a[i][j]; else y+=a[i][j]; 92 } 93 if (n*m%2==0){ 94 if (x!=y){ puts("-1"); return; } 95 RG ll l=tot,r=1LL<<50,mid; 96 while (l<=r){ 97 mid=(l+r)>>1; 98 if (check(mid)) ans=mid,r=mid-1; else l=mid+1; 99 } 100 printf("%lld\n",(ans*n*m>>1)-x); 101 } else{ 102 RG ll X=y-x; if (X<tot){ puts("-1"); return; } 103 if (check(X)) printf("%lld\n",X*((n*m)>>1)-x); 104 else puts("-1"); 105 } 106 return; 107 } 108 109 int main(){ 110 #ifndef ONLINE_JUDGE 111 freopen("game.in","r",stdin); 112 freopen("game.out","w",stdout); 113 #endif 114 RG int T=gi(); 115 while (T--) work(); 116 return 0; 117 }