【BZOJ 2756】[SCOI2012]奇怪的游戏 二分+最大流

这道题提醒我,要有将棋盘黑白染色的意识,尤其是看到相邻格子这样的条件的时候,然后就是要用到与其有关的性质与特点以体现其作用,这道题就是用到了黑格子与白格子之间的关系进行的,其出发点是每次一定会给一个黑格子与一个白格子均加一,那么最后黑白格子所加量相同(最关键的地方)。
然后呢,还要观察,最终高度与行动次数一一对应,于是求解他们两个是等效的,然后发现如果最后高度确定,是很好验证是否可行的,就是方格下水道。进一步分析,当黑格与白格的数量不同那么最终高度一定,可以一下判解。当数量相同的时候关于最终高度是单调的,因为h可以,h+1也可以,所以这道题就这么解决了。
为什么我想不到啊(苣蒻++)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define pos(a,b) (((a)-1)*m+(b))
typedef long long LL;
const int N=42;
const int P=N*N;
const int E=P*10;
const LL Inf=0x3f3f3f3f3f3f3f3fLL;
struct V{
  int to,next;
  LL f;
}c[E];
int head[P],t;
inline void add(int x,int y,LL z){
  c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].f=z;
}
inline void clear(){
  memset(head,0,sizeof(head)),t=1;
}
int n,m;
int S,T;
int deep[P],q[P],front,back;
inline bool bfs(){
  memset(deep,0,sizeof(deep));
  front=back=0,q[back++]=S,deep[S]=1;
  while(front!=back){
    int x=q[front++];
    for(int i=head[x];i;i=c[i].next)
      if(c[i].f&&deep[c[i].to]==0){
        deep[c[i].to]=deep[x]+1;
        if(c[i].to==T)return true;
        q[back++]=c[i].to;
      }
  }return false;
}
inline LL dfs(int x,LL v){
  if(x==T||v==0)return v;
  LL ret=0;
  for(int i=head[x];i;i=c[i].next)
    if(c[i].f&&deep[c[i].to]==deep[x]+1){
      LL f=dfs(c[i].to,std::min(c[i].f,v));
      ret+=f,v-=f,c[i].f-=f,c[i^1].f+=f;
      if(v==0)break;
    }
  if(ret==0)deep[x]=0;
  return ret;
}
int cnt[2];
LL sum[2];
int max,val[N][N];
inline LL dinic(){
  LL ret=0;
  while(bfs())ret+=dfs(S,Inf);
  return ret;
}
inline bool check(LL ans){
  clear();LL ret=0;
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
      if((i+j)&1){
        if(i>1)
          add(pos(i,j),pos(i-1,j),Inf),add(pos(i-1,j),pos(i,j),0);
        if(j>1)
          add(pos(i,j),pos(i,j-1),Inf),add(pos(i,j-1),pos(i,j),0);
        if(i<n)
          add(pos(i,j),pos(i+1,j),Inf),add(pos(i+1,j),pos(i,j),0);
        if(j<m)
          add(pos(i,j),pos(i,j+1),Inf),add(pos(i,j+1),pos(i,j),0);
        add(S,pos(i,j),ans-val[i][j]),add(pos(i,j),S,0);
        ret+=ans-val[i][j];
      }else
        add(pos(i,j),T,ans-val[i][j]),add(T,pos(i,j),0);
  return ret==dinic();
}
inline void work1(){
  if((sum[0]-sum[1])%(cnt[0]-cnt[1])!=0){
    puts("-1");return;
  }
  LL ans=(sum[0]-sum[1])/(cnt[0]-cnt[1]);
  if(check(ans))
    printf("%lld\n",(ans*n*m-(sum[0]+sum[1]))>>1LL);
  else puts("-1");
}
inline void work2(){
  if(sum[0]!=sum[1]){
    puts("-1");return;
  }
  LL l=max,r=Inf/5000LL,mid,ans=0;
  while(l<=r){
    mid=(l+r)>>1;
    if(check(mid))
      ans=mid,r=mid-1;
    else
      l=mid+1;
  }
  if(ans==0)puts("-1");
  else printf("%lld\n",(ans*n*m-(sum[0]+sum[1]))>>1LL);
} 
int main(){
  int test;scanf("%d",&test);
  while(test--){
    scanf("%d%d",&n,&m);
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));
    max=0,S=n*m+1,T=n*m+2;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=m;++j){
        scanf("%d",&val[i][j]);
        ++cnt[(i+j)&1],sum[(i+j)&1]+=val[i][j];
        max=std::max(max,val[i][j]);
      }
    if(cnt[0]!=cnt[1])work1();
    else work2();
  }return 0;
}

 

posted @ 2017-12-11 19:52  TS_Hugh  阅读(156)  评论(0编辑  收藏  举报