【BZOJ2756】奇怪的游戏(SCOI2012)-分类讨论+二分+最大流

测试地址:奇怪的游戏
做法:本题需要用到分类讨论+二分+最大流。
首先看到棋盘,先黑白染色,然后我们发现每次操作两个相邻的格子一定是不同色的,意味着黑白格子得到增加的权值和是一样的。那么令x为最后得到的数字,num0/1,sum0/1为黑/白色格子的数量和权值和,有:
num0xsum0=num1xsum1
解得:x=sum0sum1num0num1
num0num1的时候,我们直接解出x,那么现在我们就要判定存不存在解了。实际上,现在就是把每个点的操作次数(即xaij)分配给和它相邻的边,为了方便我们把黑色格子作为分配的点,并且使得分配完之后所有白色格子也得到正好xaij的次数。我们发现这就是一个流量平衡的关系,因此我们从源点向每个黑色格子,从每个白色格子向汇点连一条容量为xaij的边,从每个黑色格子向与它相邻的白色格子连一条容量无限大的边,那么问题有解当且仅当这个网络的最大流等于num0xsum0
那么当num0=num1时,我们知道nm为偶数,那么若sum0sum1显然无解,否则我们知道,若对于一个x它是有解的,那么对于x+1它也是有解的,因为我们显然可以找到一种方式,使得操作完后所有格子的权值都正好加1(相当于用骨牌覆盖整个棋盘)。因此我们二分x,用上述的方法判定即可。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=(1ll<<50);
int Test,n,m,S,T,first[2010],tot;
int h,t,q[2010],lvl[2010],cur[2010];
ll a[45][45],sum[2],num[2];
struct edge
{
    int v,next;
    ll f;
}e[50010];

void insert(int a,int b,ll f)
{
    e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,first[a]=tot;
    e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,first[b]=tot;
}

bool makelevel()
{
    for(int i=1;i<=T;i++)
        lvl[i]=-1,cur[i]=first[i];
    h=t=1;
    q[1]=S;
    lvl[S]=0;
    while(h<=t)
    {
        int v=q[h++];
        for(int i=first[v];i;i=e[i].next)
            if (e[i].f&&lvl[e[i].v]==-1)
            {
                lvl[e[i].v]=lvl[v]+1;
                q[++t]=e[i].v;
            }
    }
    return lvl[T]!=-1;
}

ll maxflow(int v,ll maxf)
{
    ll ret=0,f;
    if (v==T) return maxf;
    for(int i=cur[v];i;i=e[i].next)
    {
        if (e[i].f&&lvl[e[i].v]==lvl[v]+1)
        {
            f=maxflow(e[i].v,min(maxf-ret,e[i].f));
            ret+=f;
            e[i].f-=f;
            e[i^1].f+=f;
            if (ret==maxf) break;
        }
        cur[v]=i;
    }
    if (!ret) lvl[v]=-1;
    return ret;
}

bool check(ll x)
{
    memset(first,0,sizeof(first));
    tot=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if ((i+j)%2)
            {
                insert(S,(i-1)*m+j,x-a[i][j]);
                if (i>1) insert((i-1)*m+j,(i-2)*m+j,inf);
                if (i<n) insert((i-1)*m+j,i*m+j,inf);
                if (j>1) insert((i-1)*m+j,(i-1)*m+j-1,inf);
                if (j<m) insert((i-1)*m+j,(i-1)*m+j+1,inf);
            }
            else insert((i-1)*m+j,T,x-a[i][j]);
        }
    ll maxf=0;
    while(makelevel())
        maxf+=maxflow(S,inf);
    if (maxf!=num[0]*x-sum[0]) return 0;
    else return 1;
}

int main()
{
    scanf("%d",&Test);
    while(Test--)
    {
        ll maxa=0;

        scanf("%d%d",&n,&m);
        S=n*m+1,T=n*m+2;
        sum[0]=sum[1]=num[0]=num[1]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%lld",&a[i][j]);
                maxa=max(maxa,a[i][j]);
                sum[(i+j)%2]+=a[i][j],num[(i+j)%2]++;
            }

        if (num[0]!=num[1])
        {
            ll x=(sum[0]-sum[1])/(num[0]-num[1]);
            if (check(x)) printf("%lld\n",num[0]*x-sum[0]);
            else printf("-1\n");
        }
        else
        {
            if (sum[0]!=sum[1]) {printf("-1\n");continue;}
            ll l=maxa,r=inf;
            while(r>l)
            {
                ll mid=(l+r)>>1;
                if (check(mid)) r=mid;
                else l=mid+1;
            }
            printf("%lld\n",num[0]*l-sum[0]);
        }
    }

    return 0;
}
posted @ 2018-04-26 17:33  Maxwei_wzj  阅读(98)  评论(0编辑  收藏  举报