bzoj2756: [SCOI2012]奇怪的游戏(网络流+分情况)
2756: [SCOI2012]奇怪的游戏
题目:传送门
题解:
发现做不出来的大难题一点一个网络流
%大佬
首先黑白染色(原来是套路...)染色之后就可以保证每次操作都一定会使黑白各一个各自的值加1
那么我们接着统计一下黑白格子个数cnt1和cnt2,以及各自的权值和sum1和sum2
然后就要分情况讨论了:
1、在cnt1不等于cnt2的情况下
假设有解且最终的数值均为ans,那么不难发现:cnt1*ans-cnt2*ans=sum1-sum2(因为每次操作黑白格子的总和同时加1,所以总和差始终不变)
那就可以直接推导出ans=(sum1-sum2)/(cnt1-cnt2) 那么我们其实就只需要判断一下该值是否合法就OK
2、cnt1=cnt2
若sum1不等于sum2 那么一定输出-1,因为差不变啊,显然(不过好像没有这种点)
若sum1=sum2,那么设最终的数值为ans
如果ans满足答案,那么ans+1也一定满足,显然存在单调性,直接就二分check
判断ans是否合法:st连白点,流量ans-d[i][j];黑点连ed,流量ans-d[i][j];相邻的不同颜色的点相连,流量无限;那么只要看看是否满流就好啊。。。
注意上界有点大...
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define inf 999999999999999999 7 using namespace std; 8 typedef long long LL; 9 struct node 10 { 11 int x,y,next,other;LL c; 12 }a[1110000];int len,last[2100]; 13 void ins(int x,int y,LL c) 14 { 15 LL k1,k2; 16 k1=++len; 17 a[len].x=x;a[len].y=y;a[len].c=c; 18 a[len].next=last[x];last[x]=len; 19 20 k2=++len; 21 a[len].x=y;a[len].y=x;a[len].c=0; 22 a[len].next=last[y];last[y]=len; 23 24 a[k1].other=k2; 25 a[k2].other=k1; 26 } 27 int list[2100],h[2100],st,ed,head,tail; 28 bool bt_h() 29 { 30 memset(h,0,sizeof(h));h[st]=1; 31 list[1]=st;head=1;tail=2; 32 while(head!=tail) 33 { 34 int x=list[head]; 35 for(int k=last[x];k;k=a[k].next) 36 { 37 int y=a[k].y; 38 if(h[y]==0 && a[k].c>0) 39 { 40 h[y]=h[x]+1; 41 list[tail++]=y; 42 } 43 } 44 head++; 45 } 46 if(h[ed]>0)return true; 47 return false; 48 } 49 LL find_flow(int x,LL flow) 50 { 51 if(x==ed)return flow; 52 LL s=0,t; 53 for(int k=last[x];k;k=a[k].next) 54 { 55 int y=a[k].y; 56 if(h[y]==h[x]+1 && a[k].c>0 && s<flow) 57 { 58 s+=t=find_flow(y,min(a[k].c,flow-s)); 59 a[k].c-=t;a[a[k].other].c+=t;if(s==flow)break; 60 } 61 } 62 if(s==0)h[x]=0; 63 return s; 64 } 65 int T; 66 int n,m; 67 LL mp[110][110];int d[110][110]; 68 bool f[110][110];//黑false白true染色 69 const int dx[5]={0,-1,1,0,0}; 70 const int dy[5]={0,0,0,-1,1}; 71 bool check(LL sum) 72 { 73 LL ret=0; 74 len=0;memset(last,0,sizeof(last));st=n*m+1,ed=st+1; 75 for(int i=1;i<=n;i++) 76 for(int j=1;j<=m;j++) 77 if(f[i][j]==true)ins(st,d[i][j],sum-mp[i][j]),ret+=sum-mp[i][j]; 78 else ins(d[i][j],ed,sum-mp[i][j]); 79 for(int i=1;i<=n;i++) 80 for(int j=1;j<=m;j++)if(f[i][j]==true) 81 for(int k=1;k<=4;k++) 82 { 83 int tx=i+dx[k],ty=j+dy[k]; 84 if(d[tx][ty]!=-1 && f[i][j]!=f[tx][ty])ins(d[i][j],d[tx][ty],inf); 85 } 86 LL ans=0;while(bt_h())ans+=find_flow(st,inf); 87 return ret==ans?true:false; 88 } 89 LL sol(LL sum) 90 { 91 LL ans=0; 92 for(int i=1;i<=n;i++) 93 for(int j=1;j<=m;j++) 94 ans+=(sum-mp[i][j]); 95 return ans/2LL; 96 } 97 int main() 98 { 99 scanf("%d",&T);while(T--) 100 { 101 LL cnt1=0,cnt2=0,sum1=0,sum2=0,maxx=0; 102 scanf("%d%d",&n,&m);memset(mp,0,sizeof(mp));memset(d,-1,sizeof(d));memset(f,true,sizeof(f)); 103 int ss=0;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)d[i][j]=++ss; 104 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%lld",&mp[i][j]),sum1+=mp[i][j],maxx=max(maxx,mp[i][j]); 105 for(int i=1;i<=n;i++) 106 for(int j=i%2+1;j<=m;j+=2) 107 f[i][j]=false,cnt2++,sum2+=mp[i][j]; 108 sum1-=sum2;cnt1=n*m-cnt2; 109 if(cnt1==cnt2) 110 { 111 if(sum1!=sum2)printf("-1\n"); 112 else 113 { 114 LL l=maxx,r=inf,ans=-1; 115 while(l<=r) 116 { 117 LL mid=(l+r)/2; 118 //if(mid<maxx)l=mid+1; 119 if(check(mid))r=mid-1,ans=mid; 120 else l=mid+1; 121 } 122 if(ans==-1)printf("-1\n"); 123 else printf("%lld\n",sol(ans)); 124 } 125 } 126 else 127 { 128 LL ans=(sum1-sum2)/(cnt1-cnt2); 129 if(ans<maxx || check(ans)==false)printf("-1\n"); 130 else printf("%lld\n",sol(ans)); 131 } 132 } 133 return 0; 134 }