BZOJ 2756 【SCOI2012】 奇怪的游戏
题目链接:奇怪的游戏
一开始这道题想岔了……想到黑白染色后对总格子数按奇偶性分类讨论,然后没发现奇数个格子的可以直接解方程……
首先可以发现每次操作是给相邻的两个格子权值加一,因此我们把棋盘黑白染色后每次操作就是给白格子和黑格子加一。因此白格子和黑格子的增加的量是相等的。设我们有\(n_1\)个白格子,它们的数值和是\(s_1\),有\(n_2\)个黑格子,数值和是\(s_2\)。再设最后每个数都变成了\(x\),那么有:
\[n_1x-s_1=n_2x-s_2\]
所以当\(n_1 \neq n_2\)时就可以直接解出\(x\)了。然后跑遍最大流检验一下这个\(x\)是否合法即可。
然后当\(n_1 = n_2\)时,如果\(s_1 \neq s_2\),那么显然无解。否则的话,我们可以发现答案是可以二分的。若\(x\)是一个合法的答案,因为共有偶数个格子,那么行数和列数至少有一个是偶数,我们就有一种方案把每个位置上的权值都加上一,使答案变成\(x+1\)。所以,二分一个答案,最大流\(check\)即可。
下面贴代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define maxn 1610 #define maxm 10000 #define INF (1ll<<60) using namespace std; typedef long long llg; int Q,n,m,a[41][41],S,T,n1,n2,_m; int zx[4]={0,0,1,-1},zy[4]={1,-1,0,0}; int head[maxn],next[maxm],to[maxm],tt; llg s1,s2,c[maxm]; int getint(){ int w=0;bool q=0; char c=getchar(); while((c>'9'||c<'0')&&c!='-') c=getchar(); if(c=='-') c=getchar(),q=1; while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } void link(int x,int y){ to[++tt]=y;next[tt]=head[x];head[x]=tt; to[++tt]=x;next[tt]=head[y];head[y]=tt; } int d[maxn],rd,ld,dep[maxn]; bool bfs(){ for(int i=1;i<=T;i++) dep[i]=-1; ld=rd=0; d[rd++]=S; dep[S]=1; while(ld!=rd){ int u=d[ld++]; for(int i=head[u],v;v=to[i],i;i=next[i]) if(c[i] && dep[v]==-1) dep[v]=dep[u]+1,d[rd++]=v; } return dep[T]!=-1; } llg dfs(int u,llg now){ if(!now) return 0; if(u==T) return now; llg low=0,res; for(int i=head[u],v;v=to[i],i;i=next[i]) if(c[i] && dep[v]==dep[u]+1){ res=dfs(v,min(now,c[i])); low+=res; c[i]-=res; c[i^1]+=res; now-=res; } if(!low) dep[u]=-1; return low; } bool cheadck(llg x){ int next=2; llg ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++,next+=2){ if(a[i][j]>x) return 0; c[next]=x-a[i][j],c[next^1]=0; } while(next<=tt) c[next]=INF,c[next^1]=0,next+=2; while(bfs()) ans+=dfs(S,INF); return ans>=1ll*x*n1-s1; } void lian(int u,int x,int y){ for(int k=0,i,j;k<4;k++){ i=x+zx[k],j=y+zy[k]; if(i>0 && i<=n && j>0 && j<=m) link(u,(i-1)*m+j); } } int main(){ File("a"); Q=getint(); while(Q--){ n=getint(); m=getint(); _m=n1=n2=0; S=n*m+1; T=S+1; tt=1; s1=s2=0; for(int i=1,now=1;i<=n;i++) for(int j=1;j<=m;j++,now++){ _m=max(_m,a[i][j]=getint()); if((i+j)&1) link(S,now),s1+=a[i][j],n1++; else link(now,T),s2+=a[i][j],n2++; } for(int i=1,now=1;i<=n;i++) for(int j=1;j<=m;j++,now++) if((i+j)&1) lian(now,i,j); if(n1!=n2){ llg x=(s1-s2)/(n1-n2); if(cheadck(x)) printf("%lld\n",n1*x-s1); else printf("-1\n"); } else if(s1!=s2) printf("-1\n"); else{ llg l=_m,r=s1+s2,mid; while(l!=r){ mid=(l+r)>>1; if(cheadck(mid)) r=mid; else l=mid+1; } printf("%lld\n",l*n1-s1); } for(int i=1;i<=T;i++) head[i]=0; } return 0; }