BZOJ2756 SCOI2012奇怪的游戏(二分答案+最大流)
由数据范围容易想到网络流。由于操作只是对于棋盘上相邻两格,容易想到给其黑白染色。
假设已经知道最后要变成什么数。那么给黑白点之间连边,其流量则表示同时增加的次数,再用源汇给其限流为需要增加的数即可。
考虑最后应该变成什么数。
如果棋盘中黑白格子数量不同,设最后变成的数是x,则x*黑格数量-黑格数字和=x*白格数量-白格数字和,若黑格数量多即x=黑格数字和-白格数字和。直接变为最大值是不一定合法的。
否则首先黑白格子内权值和应相同,否则无解。如果变成某个数是合法的,变的更大也是合法的。那么二分最后变成的数即可。因为最终方案中并不一定存在能恰好覆盖整个棋盘的操作,所以直接变为最大数是错的。注意二分上界需要开的非常大。
无数次死于long long。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 42 #define S 0 #define T 1601 #define inf 2000000000000 int test,n,m,p[N*N],a[N][N],t; int d[N*N],q[N*N],cur[N*N]; bool flag[N*N]; long long ans; struct data{int to,nxt;long long cap,flow; }edge[N*N<<5]; void addedge(int x,int y,long long z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t; } int trans(int x,int y){return (x-1)*m+y;} bool bfs() { memset(d,255,sizeof(d));d[S]=0; int head=0,tail=1;q[1]=S; do { int x=q[++head]; for (int i=p[x];~i;i=edge[i].nxt) if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+1; q[++tail]=edge[i].to; } }while (head<tail); return ~d[T]; } long long work(int k,long long f) { if (k==T) return f; long long used=0; for (int i=cur[k];~i;i=edge[i].nxt) if (d[k]+1==d[edge[i].to]) { long long w=work(edge[i].to,min(edge[i].cap-edge[i].flow,f-used)); edge[i].flow+=w,edge[i^1].flow-=w; if (edge[i].flow<edge[i].cap) cur[k]=i; used+=w;if (used==f) return f; } if (used==0) d[k]=-1; return used; } void dinic() { ans=0; while (bfs()) { memcpy(cur,p,sizeof(p)); ans+=work(S,inf); } } bool check(long long k,long long lim) { t=-1;memset(p,255,sizeof(p)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (i+j&1) { addedge(S,trans(i,j),k-a[i][j]); if (i>1) addedge(trans(i,j),trans(i-1,j),inf); if (i<n) addedge(trans(i,j),trans(i+1,j),inf); if (j>1) addedge(trans(i,j),trans(i,j-1),inf); if (j<m) addedge(trans(i,j),trans(i,j+1),inf); } else addedge(trans(i,j),T,k-a[i][j]); dinic(); return ans==lim; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2756.in","r",stdin); freopen("bzoj2756.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif test=read(); while (test--) { n=read(),m=read();int v=0; long long sumx=0,sumy=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { v=max(v,a[i][j]=read()); if (i+j&1) sumx+=a[i][j]; else sumy+=a[i][j]; } long long tot=-1; if ((n&1)&&(m&1)) tot=sumy-sumx>=v?(check(sumy-sumx,(sumy-sumx)*n*m-sumx-sumy>>1)?sumy-sumx:-1):-1; else { if (sumx==sumy) { long long l=v,r=inf; while (l<=r) { long long mid=l+r>>1; if (check(mid,mid*n*m-sumx-sumy>>1)) tot=mid,r=mid-1; else l=mid+1; } } } if (tot==-1) cout<<-1<<endl; else cout<<(tot*n*m-sumx-sumy>>1)<<endl; } return 0; }