bzoj 2756: [SCOI2012]奇怪的游戏

题意

Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。

题解

复习了一下这个题。。
感觉还是挺好的
一个十分显然的想法就是黑白染色啊
然后假设两个颜色的格子个数分别是 a,b a , b ,,和是 A,B A , B
如果 ab a ≠ b
设答案是ans
那么必然满足 A+aans=B+bans A + a ∗ a n s = B + b ∗ a n s
可以得到 ans=(AB)/(ba) a n s = ( A − B ) / ( b − a )
直接判断就可以了
但是我们可以发现,如果a=b,那么分母就会变成0,就不可以用这个式子了
但是,我们发现,如果a=b,那么答案是满足二分性的,我们可以通过二分答案来解决这个问题

然后判是否可行,用一个网络流就可以了

以前的CODE

#include<cstdio>
#include<cstdlib>
#include<cstring>
const long long N=45*45;
const long long MAX=1LL<<50;
long long T;
long long a[45][45];
long long n,m;
long long num1,num2;//黑格子的个数  白格子的个数 
long long tot1,tot2;//黑格子的总和  白格子的总和
long long ooo;
long long mymin (long long x,long long y){return x<y?x:y;}
long long mymax (long long x,long long y){return x>y?x:y;}
void init ()
{
    ooo=0;
    num1=num2=tot1=tot2=0;
    scanf("%lld%lld",&n,&m);
    for (long long u=1;u<=n;u++)
        for (long long i=1;i<=m;i++)
        {
            scanf("%lld",&a[u][i]);
            ooo=mymax(ooo,a[u][i]);
            if ((u+i)%2==0) {tot1+=a[u][i];num1++;}
            else {tot2+=a[u][i];num2++;}
        }
}
struct qq
{
    long long x,y,z;
    long long other,last;
}s[20005];
long long last[N],num;
long long init1 (long long x,long long y,long long z)
{
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
    return num;
}
void init (long long x,long long y,long long z)
{
    long long Num1=init1(x,y,z),Num2=init1(y,x,0);
    s[Num1].other=Num2;s[Num2].other=Num1;
    return ;
}
long long st1,ed1;
long long q[N],h[N];
bool bt ()
{
    memset(h,-1,sizeof(h));
    long long st=1,ed=2;
    q[st]=st1;h[st1]=0;
    while (st!=ed)
    {
        long long x=q[st];
        for (long long u=last[x];u!=-1;u=s[u].last)
        {
            long long y=s[u].y;
            if (h[y]==-1&&s[u].z>0)
            {
                h[y]=h[x]+1;
                q[ed]=y;
                ed++;
            }
        }
        st++;
    }
    if (h[ed1]==-1) return false;
    return true;
}
long long find (long long x,long long f)
{
    /*printf("%lld %lld\n",x,f);
    system("pause");*/
    if (x==ed1) return f;
    long long s1=0;
    for (long long u=last[x];u!=-1;u=s[u].last)
    {
        long long y=s[u].y;
        if (s[u].z>0&&h[y]==(h[x]+1)&&s1<f)
        {
            long long tt=find(y,mymin(s[u].z,f-s1));
            s1+=tt;
            s[u].z-=tt;
            s[s[u].other].z+=tt;
        }
    }
    if (s1==0) h[x]=-1;
    return s1;
}
bool check (long long x)//最后变为x是不是可以 
{
    st1=n*m+1;ed1=st1+1;
    bool tf=false;
    long long ans=0;
    num=0;memset(last,-1,sizeof(last));
    for (long long u=1;u<=n;u++)
    {
        for (long long i=1;i<=m;i++)
        {
            if ((u+i)%2==0)
            {
                init(st1,(u-1)*m+i,x-a[u][i]);
                if (u>1) init((u-1)*m+i,(u-2)*m+i,MAX);
                if (i>1) init((u-1)*m+i,(u-1)*m+i-1,MAX);
                if (u<n) init((u-1)*m+i,u*m+i,MAX);
                if (i<m) init((u-1)*m+i,(u-1)*m+i+1,MAX);
            }
            else
            {
                init((u-1)*m+i,ed1,x-a[u][i]);
                ans=ans+x-a[u][i];
            }
        }
    }
    /*for (long long u=1;u<=num;u++)
        if (s[u].z!=0)
            printf("%lld %lld %lld\n",s[u].x,s[u].y,s[u].z);*/
    long long tt=0;
    while (bt()) tt=tt+find(st1,MAX);
    if (tt==ans) return true;
    return false;
}
void print (long long x)
{
    long long ans=0;
    for (long long u=1;u<=n;u++)
    {
        for (long long i=1;i<=m;i++)
        {
            if ((u+i)%2==0) ans=ans+x-a[u][i];
        }
    }
    printf("%lld\n",ans);
}
void solve1 ()
{
    //if (check(3)==true) printf("YES");
    if (tot1!=tot2) 
    {
        printf("-1\n");
        return ;
    }
    long long l=ooo,r=MAX;
    long long ans=-1;
    while (l<=r)
    {

        long long mid=(l+r)/2;
        //printf("%lld %lld %lld\n",l,r,mid);
        if (check(mid)==true) {ans=mid;r=mid-1;}
        else l=mid+1;
    }
    if (ans==-1) printf("-1\n");
    else print(ans);
}
void solve2 ()
{

    long long x=(tot1-tot2)/(num1-num2);
    if (x<ooo) {printf("-1\n");return ;}
    if(check(x)==true) print(x);
    else printf("-1\n");
    return ;
}
void solve ()
{
    if (num1==num2) solve1();
    else solve2();
}
int main()
{
    scanf("%lld",&T);
    while (T--)
    {
        init();
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值