您的位置:首页 > 其它

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

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

;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: