【bzoj 2756】奇怪的游戏(最大流)

传送门biu~
对棋盘进行黑白染色。设黑格个数为num0,数值和为sum0;白格个数为num1 数值和为sum1。
最后都变为x,则num0 * x – sum0 = num1 * x – sum1,所以x = (sum0 – sum1) / (num0 – num1)。当num0 ≠ num1时,可以解出 x 再用网络流验证;当num1 = num2时,可以发现对于一个合法的x k>=x都是一个合法的解。所以可以二分x,再用网络流验证。
建图时,如果点k为白,建边(s, k, x – v[k]);如果为黑,建边(k, t, x – v[k]);相邻点u、v (u为白)
建边 (u, v, INF)。

#include<bits/stdc++.h>
using namespace std;
const long long INF=1LL<<50;
long long sum0,sum1;
int num0,num1;
int n,m,cnt,S,T;
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
int a[45][45],color[45][45];
int head[2005],fir[2005],dep[2005],nex[20005],to[20005],tp;
long long cap[20005];   
inline void add(int x,int y,long long c){
    nex[++tp]=head[x];
    head[x]=tp;
    to[tp]=y;
    cap[tp]=c;
}
inline void Insert(int u,int v,long long w){add(u,v,w);add(v,u,0);}
inline int bfs(){
    memset(dep,0,sizeof(dep));
    dep[S]=1;queue<int>q;
    q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nex[i]){
            if(cap[i] && !dep[to[i]]){
                dep[to[i]]=dep[x]+1;
                q.push(to[i]);
            }
        }
    }
    return dep[T];
}
long long dfs(int x,long long now){
    if(x==T || !now)    return now;
    long long c=0;
    for(int &i=fir[x];i;i=nex[i]){
        if(dep[to[i]]==dep[x]+1 && cap[i]){
            long long f=dfs(to[i],min(now,cap[i]));
            now-=f;
            cap[i]-=f;
            cap[i^1]+=f;
            c+=f;
            if(!now)    break;
        }
    }
    return c;
}
inline long long Dinic(){
    long long c=0;
    while(bfs()){
        for(int i=S;i<=T;++i) fir[i]=head[i];
        c+=dfs(S,INF);
    }
    return c;
}
inline bool check(long long x){
    memset(head,0,sizeof(head));tp=1;
    S=0;T=n*m+1;
    long long tot=0;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(color[i][j]){
                Insert(S,(i-1)*m+j,x-a[i][j]);tot+=x-a[i][j];
                for(int k=0;k<4;k++){
                    int nowx=i+xx[k],nowy=j+yy[k];
                    if(nowx<1||nowy<1||nowx>n||nowy>m)continue;
                    Insert((i-1)*m+j,(nowx-1)*m+nowy,INF);
                }
            }
            else Insert((i-1)*m+j,T,x-a[i][j]);
    return (Dinic()==tot);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        num0=num1=sum0=sum1=0;
        scanf("%d%d",&n,&m);
        int Max=0;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                scanf("%d",&a[i][j]);
                color[i][j]=(i+j)&1;
                Max=max(Max,a[i][j]);
            }
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                if(color[i][j])sum1+=a[i][j],++num1;
                else sum0+=a[i][j],++num0;
            }
        }
        if(num0!=num1){
            long long x=(sum0-sum1)/(num0-num1);
            if(x>=Max){
                if(check(x)){
                    printf("%lld\n",x*num1-sum1);
                    continue;
                }
            }
            printf("-1\n");
        }
        else{
            if(sum0!=sum1){
                printf("-1\n");
                continue;
            }
            long long l=Max,r=INF;
            while(l<=r){
                long long mid=l+r>>1;
                if(check(mid))  r=mid-1;
                else            l=mid+1;
            }
            printf("%lld\n",l*num1-sum1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zP1nG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值