bzoj2756 [SCOI2012]奇怪的游戏

本文介绍了一个在N*M棋盘上进行的游戏,目标是最少次数使棋盘数字相同,通过黑白染色分析和网络流验证等方法解决。适用于30%数据规模较小和100%数据规模较大情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/2018/01/08/bzoj2756-scoi2012%e5%a5%87%e6%80%aa%e7%9a%84%e6%b8%b8%e6%88%8f/
Description

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

输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
Output

对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
Sample Input
2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
2
-1

HINT

【数据范围】

对于30%的数据,保证 T<=10,1<=N,M<=8

对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000

首先拿到棋盘我们首先考虑黑白染色有没有用 这题是有用的 怎么用 我们假设黑白染色之后黑色是num1 白色是num2那么我们来看 如果这个棋盘中格子的个数是奇数个那么答案我们是可以直接计算的 如何搞 那就假设我同时都取到的最大值为x吧 那么 因为我每次都给两个染色 所以黑白格子增加的总和一定一样多 那么意味着如果他们的是存在差值的 那么最后加起来也一定回存在差值

可写出方程num1*x-num2*x=sum1-sum2 x=(sum1-sum2)/(num1-num2)

那么很好 算出来这个最大值了我用网络流验证一下即可 如果这个值小于我当前棋盘中最大的数值 那么说明做减法了 显然是不对的输出-1 否则 每条边的边权建为x-mp[i][j]即他们的差值 每个黑格子再向四周的白格子建inf的边 最后看是否满流验证一下

如果格子数是偶数 那么sum1一定要求和sum2相同 如果不同一定不满足条件 (同理:每次相当于黑色区域和白色区域增加的是相同多的数)然后这时候我就需要二分去算一下我这个最后的答案是多少了 就贰分一下我最后答案是多少 然后同奇数格子一样跑最大流验证下 如果成功了就缩小范围反之亦然 注意二分的时候mid也得是longlong gg我好菜啊卡评测了两发gg

建图的话我是先把所有的图都建起来然后每次去修改权值就okay但是吧显然速度还是挺慢的可能因为我是蒟蒻 自带大常数吧Orz 囧rz

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 1LL<<50
#define N 2000
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x;
}
struct node{
    int y,next;ll z;
}data[N<<3];
int num=1,level[N],h[N],n,m,id[55][55],mp[55][55],TT,T,cur[N];
inline void insert1(int x,int y,ll z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;
}
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
inline bool bfs(){
    queue<int>q;memset(level,0,sizeof(level));level[0]=1;q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;ll z=data[i].z;
            if (level[y]||!z) continue;level[y]=level[x]+1;if (y==T) return 1;q.push(y);
        }
    }return 0;
}
inline ll dfs(int x,ll s){
    if (x==T) return s;ll ss=s;
    for (int &i=cur[x];i;i=data[i].next){
        int y=data[i].y;ll z=data[i].z;
        if (level[x]+1==level[y]&&z){
            ll xx=dfs(y,min(s,z));if (!xx) level[y]=0;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if(!s) return ss;
        }
    }return ss-s;
}
inline bool check(ll mid){
    ll sum=0;int nm=1;
    for (int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            if (i+j&1){
                data[++nm].z=mid-mp[i][j];data[++nm].z=0;sum+=mid-mp[i][j];
            }else{
                data[++nm].z=mid-mp[i][j];data[++nm].z=0;
            }
        } 
    }ll ans=0;
    for (int i=nm+1;i<=num;++i) if ((i&1)==0) data[i].z=inf;else data[i].z=0;
    while(bfs()) memcpy(cur,h,sizeof(h)),ans+=dfs(0,inf);
    if (sum==ans) return 1;else return 0;
}
int main(){
    //freopen("bzoj2756.in","r",stdin);
    TT=read();
    while(TT--){
        n=read();m=read();int tot=0,num1=0,num2=0,max1=0;ll sum1=0,sum2=0;memset(h,0,sizeof(h));num=1;
        for (int i=1;i<=n;++i)
            for (int j=1;j<=m;++j) {
                id[i][j]=++tot; mp[i][j]=read();max1=max(max1,mp[i][j]);
                if (i+j&1) sum1+=mp[i][j],++num1;else sum2+=mp[i][j],++num2;
            }T=tot+1;
        for (int i=1;i<=n;++i)
            for (int j=1;j<=m;++j)
                if (i+j&1) insert1(0,id[i][j],0);else insert1(id[i][j],T,0);
        for (int i=1;i<=n;++i){
            for (int j=1;j<=m;++j){
                if (i+j&1) {
                    for (int k=0;k<4;++k){
                        int x1=i+dx[k],y1=j+dy[k];
                        if (x1<1||x1>n||y1<1||y1>m) continue;
                        insert1(id[i][j],id[x1][y1],inf);
                    }
                }
            }
        }
        if (tot&1){
            ll x=sum2-sum1;
            if (x<max1||!check(x)) {puts("-1");continue;} 
            printf("%lld\n",x*num1-sum1);continue;
        } ll l=max1,r=inf;
        if (sum1!=sum2) {puts("-1");continue;}
        while(l<=r){
            ll 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值