SCOI2012 奇怪的遊戲
阿新 • • 發佈:2018-12-25
在中醫藥的\(ACM\)中做到了,是\(M\)題.
那場比賽被打自閉了\(QAQ\)
題目連結
這道題一看就知道是一個網路流模型
建模也是比較典型的方式.
因為我們選兩個相鄰的格子,因此我們把它黑白染色,記錄\(4\)個量:白點個數\(c_w\),黑點個數\(c_b\),白點之和\(s_w\),黑點之和\(s_b\).
假設最終所有的高度都是\(h\).一次操作會把一個黑點\(+1\),也會把一個白點\(+1\).
我們列出方程:\(h*c_b-s_b=h*c_w-s_w\)
簡單移個項得\(h*(c_b-c_w)=s_b-s_w\)
那麼,\(h=\frac{s_b-s_w}{c_b-c_w}\)
假設\(c_b-c_w\)不為\(0\),如果\(s_b-s_w\)不為\(c_b-c_w\)的倍數就無解.
否則就把\(h\)算出來然後\(check\)一下.
具體如何\(check\)後文會說.
如果\(c_b-c_w=0\),那麼如果\(s_b-s_w\)不為\(0\)也無解.
如果為\(0\)怎麼辦呢?
首先,若\(c_b-c_w=0\),那麼\(n,m\)中肯定有一個偶數.
假設我們可以找到一個答案\(x\),那麼\(x+1\)也肯定是答案(因為\(n,m\)中有一個偶數)
因此我們可以二分.
首先二分一個\(x\),然後用網路流\(check\).
這個和上文的\(check\)是一樣的.
我們讓源點\(S\)
然後我們讓所有黑點和相鄰的白點連邊,容量是\(INF\)
然後我們讓所有白點\((i,j)\)向匯點\(T\)連邊,容量是\(x-w_{i,j}\)
對於每單位的流,肯定通過某個黑點,某個白點,然後到\(T\).
這等價於我們操作一次.
那麼只要記錄一下\(V=\sum_{(i,j)is\ black}x-w_{i,j}\)
然後跑一個最大流,看看答案是不是\(V\)即可.
程式碼如下
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #define N (1621) #define M (4000001) #define inf (1e16) #define rg register int #define Label puts("NAIVE") #define spa print(' ') #define ent print('\n') #define rand() (((rand())<<(15))^(rand())) typedef long double ld; typedef long long LL; typedef unsigned long long ull; using namespace std; inline char read(){ static const int IN_LEN=1000000; static char buf[IN_LEN],*s,*t; return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++); } template<class T> inline void read(T &x){ static bool iosig; static char c; for(iosig=false,c=read();!isdigit(c);c=read()){ if(c=='-')iosig=true; if(c==-1)return; } for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0'); if(iosig)x=-x; } inline char readchar(){ static char c; for(c=read();!isalpha(c);c=read()) if(c==-1)return 0; return c; } const int OUT_LEN = 10000000; char obuf[OUT_LEN],*ooh=obuf; inline void print(char c) { if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf; *ooh++=c; } template<class T> inline void print(T x){ static int buf[30],cnt; if(x==0)print('0'); else{ if(x<0)print('-'),x=-x; for(cnt=0;x;x/=10)buf[++cnt]=x%10+48; while(cnt)print((char)buf[cnt--]); } } inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);} #define int LL int W,n,m,a[41][41],fi[N],ne[M],b[M],S,T,q[N],h,t,d[N],vh[N],E,c[M]; void add(int x,int y,LL z){ ne[++E]=fi[x],fi[x]=E,b[E]=y,c[E]=z; ne[++E]=fi[y],fi[y]=E,b[E]=x,c[E]=0; } void getdis(){ h=t=0,q[++t]=T; while(h<t){ int u=q[++h]; for(int i=fi[u];i;i=ne[i]){ int v=b[i]; if(d[v]||v==T)continue; d[v]=d[u]+1,q[++t]=v; } } } int dfs(int u,LL Flow){ if(u==T)return Flow; LL res=Flow; int mindist=n*m+2; for(int i=fi[u];i;i=ne[i]) if(c[i]){ int v=b[i],nega=i^1; if(d[u]==d[v]+1){ int x=dfs(v,min(res,1ll*c[i])); res-=x,c[i]-=x,c[nega]+=x; if(d[S]==n)return Flow-res; if(!res)break; } mindist=min(d[v],mindist); } if(Flow==res){ vh[d[u]]--; if(!vh[d[u]])d[S]=n*m+2; else d[u]=mindist+1,vh[d[u]]++; } return Flow-res; } bool check(LL x){ int cnt=0;LL V=0; E=1; S=n*m+1,T=(n*m)+2; memset(fi,0,sizeof(fi)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cnt++; if(((i+j)&1)==0){ V+=1ll*(x-a[i][j]); add(S,cnt,x-a[i][j]); if(j<m)add(cnt,cnt+1,inf); if(i<n)add(cnt,cnt+m,inf); if(j>1)add(cnt,cnt-1,inf); if(i>1)add(cnt,cnt-m,inf); } else add(cnt,T,x-a[i][j]); } for(int i=1;i<=n*m+2;i++)d[i]=0; getdis(); LL ans=0; for(int i=0;i<=n*m+2;i++)vh[i]=0; for(int i=1;i<=n*m+2;i++)vh[d[i]]++; while(d[S]<n*m+2)ans+=dfs(S,inf); return (ans==V); } signed main(){ read(W); while(W--){ int cntb=0,cntw=0,sb=0,sw=0,mx=0; read(n),read(m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ read(a[i][j]),mx=max(mx,a[i][j]); if(!((i+j)&1))cntb++,sb+=a[i][j]; else cntw++,sw+=a[i][j]; } if(cntb!=cntw){ int x=(sb-sw)/(cntb-cntw); if(x>=mx&&check(x))print(x*cntw-sw),ent; else print(-1),ent; continue; } if(sb!=sw){print(-1),ent;continue;} LL L=mx,R=inf/2,ans=-1; while(L<=R){ LL mid=(L+R)>>1; if(check(mid))R=mid-1,ans=mid; else L=mid+1; } print(ans*cntw-sw),ent; } return flush(),0; }