[bzoj2756]奇怪的游戏
对棋盘黑白染色后,若n和m都是奇数(即白色和黑色点数不同),可以直接算得答案(根据白-黑不变);若n和m不都是奇数,二分答案(二分的上限要大一点,开$2^50$),最后都要用用网络流来判定。
考虑判定,将白色点放在左边,黑色点放在右边,源点流向白点的流量是白点与答案的差,黑点流向汇点的流量是黑点与答案的差,白点向每一个相邻的黑点流inf的边,判断是否满流即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2005 4 #define ll long long 5 #define inf (1LL<<50) 6 struct ji{ 7 int nex,to; 8 ll len; 9 }edge[N*5]; 10 queue<int>q; 11 int E,T,t,n,m,a[51][51],id[51][51],head[N],work[N],d[N]; 12 ll l,r,s1,s2,mid; 13 void add(int x,int y,ll z){ 14 edge[E].nex=head[x]; 15 edge[E].to=y; 16 edge[E].len=z; 17 head[x]=E++; 18 if (E&1)add(y,x,0); 19 } 20 bool bfs(){ 21 q.push(0); 22 memset(d,-1,sizeof(d)); 23 d[0]=0; 24 while (!q.empty()){ 25 int k=q.front(); 26 q.pop(); 27 for(int i=head[k];i!=-1;i=edge[i].nex) 28 if ((edge[i].len)&&(d[edge[i].to]<0)){ 29 d[edge[i].to]=d[k]+1; 30 q.push(edge[i].to); 31 } 32 } 33 return d[T]>=0; 34 } 35 ll dfs(int k,ll s){ 36 if (k==T)return s; 37 ll p; 38 for(int i=work[k];i!=-1;i=edge[i].nex) 39 if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){ 40 p=dfs(edge[i].to,min(s,edge[i].len)); 41 if (p){ 42 edge[i].len-=p; 43 edge[i^1].len+=p; 44 work[k]=i; 45 return p; 46 } 47 } 48 work[k]=-1; 49 return 0; 50 } 51 ll dinic(){ 52 ll k,ans=0; 53 while (bfs()){ 54 memcpy(work,head,sizeof(head)); 55 while (k=dfs(0,inf))ans+=k; 56 } 57 return ans; 58 } 59 bool pd(ll k){ 60 memset(head,-1,sizeof(head)); 61 E=0; 62 for(int i=1;i<=n;i++) 63 for(int j=1;j<=m;j++) 64 if ((i+j)&1){ 65 add(0,id[i][j],k-a[i][j]); 66 if (i>1)add(id[i][j],id[i-1][j],inf); 67 if (j>1)add(id[i][j],id[i][j-1],inf); 68 if (i<n)add(id[i][j],id[i+1][j],inf); 69 if (j<m)add(id[i][j],id[i][j+1],inf); 70 } 71 else add(id[i][j],T,k-a[i][j]); 72 return dinic()==k*(n*m/2)-s1; 73 } 74 int main(){ 75 scanf("%d",&t); 76 while (t--){ 77 scanf("%d%d",&n,&m); 78 for(int i=1;i<=n;i++) 79 for(int j=1;j<=m;j++)scanf("%d",&a[i][j]); 80 for(int i=1;i<=n;i++) 81 for(int j=1;j<=m;j++)id[i][j]=(i-1)*m+j; 82 T=id[n][m]+1; 83 s1=s2=l=0; 84 for(int i=1;i<=n;i++) 85 for(int j=1;j<=m;j++){ 86 l=max(l,1LL*a[i][j]); 87 if ((i+j)&1)s1+=a[i][j]; 88 else s2+=a[i][j]; 89 } 90 if ((n&1)&&(m&1)){ 91 if ((s2-s1<l)||(!pd(s2-s1)))printf("-1\n"); 92 else printf("%lld\n",(s2-s1)*(n*m/2)-s1); 93 continue; 94 } 95 if (s1!=s2){ 96 printf("-1\n"); 97 continue; 98 } 99 r=inf; 100 while (l<r){ 101 mid=(l+r>>1); 102 if (pd(mid))r=mid; 103 else l=mid+1; 104 } 105 if (!pd(l))printf("-1\n"); 106 else printf("%lld\n",l*n*m/2-s1); 107 } 108 }