BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 1594  Solved: 396
[Submit][Status][Discuss]

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

题解:

我们把整个棋盘的格子分为两种,一种为白,一种为黑

然后设最后的格子全部变成了x,那么x*num1-sum1=x*num2-sum2;

其中num1为白色格子数量,num2位黑色格子数量,sum1为白色格子权值和

那么,我们可以得到x=(sum1-sum2)/(num1-num2)

当num1!=num2的时候,我们可以直接通过最大流来check

否则的话,我们就二分枚举答案

首先如果x能够成立的话,那么大与x的所有数都能成立,只要再铺一层就好,而且num1+num2%2==0

所以题目的思路还是比较简单的~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<ctime>
#include<vector>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#define inf (1LL<<50)
#define pa pair<int,int>
#define ll long long
#define p(x,y) (x-1)*m+y
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
ll s0,s1;
int c0,c1;
int test,n,m,cnt,S,T;
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
int a[45][45];
int last[2005],h[2005],q[2005],cur[2005];
bool color[45][45];
struct edge{
    int to,next;ll v;
}e[20005];
void insert(int u,int v,ll w)
{
    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;e[cnt].v=w;
    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;e[cnt].v=0;
}
bool bfs()
{
    int head=0,tail=1;
    memset(h,-1,sizeof(h));
    q[0]=S;h[S]=0;
    while(head!=tail)
    {
        int now=q[head];head++;
        for(int i=last[now];i;i=e[i].next)
            if(e[i].v&&h[e[i].to]==-1)
            {
                h[e[i].to]=h[now]+1;
                q[tail++]=e[i].to;
            }
    }
    return h[T]!=-1;
}
ll dfs(int x,ll f)
{
    if(x==T)return f;
    ll w,used=0;
    for(int i=cur[x];i;i=e[i].next)
        if(h[e[i].to]==h[x]+1)
        {
            w=dfs(e[i].to,min(f-used,e[i].v));
            e[i].v-=w;e[i^1].v+=w;
            if(e[i].v)cur[x]=i;
            used+=w;if(used==f)return f;
        }
    if(!used)h[x]=-1;
    return used;
}
ll dinic()
{
    ll tmp=0;
    while(bfs())
    {
        for(int i=S;i<=T;i++)cur[i]=last[i];
        tmp+=dfs(S,inf);
    }
    return tmp;
}
bool check(ll x)
{
    memset(last,0,sizeof(last));
    cnt=1;S=0;T=n*m+1;
    ll tot=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(color[i][j])
            {
                insert(S,p(i,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(p(i,j),p(nowx,nowy),inf);
                }
            }
            else insert(p(i,j),T,x-a[i][j]);
    if(dinic()==tot)return 1;
    return 0;
}
int main()
{
    test=read();
    while(test--)
    {
        c0=c1=s0=s1=0;
        n=read();m=read();
        int mx=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                a[i][j]=read(),color[i][j]=(i+j)&1;
                mx=max(mx,a[i][j]);
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(color[i][j])s1+=a[i][j],c1++;
                else s0+=a[i][j],c0++;
        if(c0!=c1)
        {
            ll x=(s0-s1)/(c0-c1);
            if(x>=mx)
                if(check(x))
                {
                    printf("%lld\n",x*c1-s1);
                    continue;
                }
            puts("-1");
        }
        else
        {
            ll l=mx,r=inf;
            while(l<=r)
            {
                ll mid=(l+r)>>1;
                if(check(mid))r=mid-1;
                else l=mid+1;
            }
            printf("%lld\n",(ll)l*c1-s1);
        }
    }
    return 0;
}
时间: 2024-12-23 22:46:22

BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分的相关文章

BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3352  Solved: 919[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

BZOJ 2756: [SCOI2012]奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3410  Solved: 941[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成

BZOJ 2756 SCOI2012 奇怪的游戏 最大流

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. Input 输入的第一行是一个整数T,表示输入数据有T轮游戏组成. 每轮游戏的第一行有两

BZOJ 2756 SCOI 奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 4978  Solved: 1381[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变

[SCOI2012]奇怪的游戏 (网络流)

Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. Input 输入的第一行是一个整数T,表示输入数据有T轮游戏组成. 每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数. 接下来有N行,每行 M个数. Output 对于每个游戏输出最少能使游戏

【BZOJ2756】奇怪的游戏(二分,最小割)

题意: Blinker最近喜欢上一个奇怪的游戏.这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1.现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. n,m<=40 a[i,j]<=10^9 思路:假设我们已经询问最终是否都能变成数字D怎么做? 注意到黑白点染色后每次加的点必定是1黑点1白点 设黑格子有k1个,和为s1,白格子有k2个,和为s2 显然k1*d-s1=k2

BZOJ2756: [SCOI2012]奇怪的游戏

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 对棋盘进行黑白染色.若答案为x,有c1*x-s1==c2*x-s2得x=(s1-s2)/(c1-c2),那么若c1!=c2,检测x就可以了.若不是就二分x. 建图:s->黑点 c=x-a[i][j] 白点->t c=x-a[i][j] 黑->白 c=inf #include<cstring> #include<iostream> #include<

SCOI2012 奇怪的游戏

题目链接:戳我 怎么说呢,看到棋盘(应该想到二分图染色) 设白色格子数量为\(cnt0\),现在的值的和为\(sum0\).黑色格子的数量为\(cnt1\),现在的值的和为\(sum1\),最后的答案为x. \(sum0+cnt0*x=sum1+cnt1*x\) \(sum0-sum1=x*(cnt1-cnt0)\) 如果\(cnt1-cnt0\)不为0的时候,我们可以直接获得答案,然后验证答案的正确性. 我们不容易知道到底最后的终止数为多少,但是我们知道如果格子数量是偶数的话,如果一个x成立,

bzoj 2756奇怪的游戏

2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 2571  Solved: 685[Submit][Status][Discuss] Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1. 现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成