[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 }
View Code

 

posted @ 2019-11-11 13:10  PYWBKTDA  阅读(119)  评论(0编辑  收藏  举报