您的位置:首页 > 其它

bzoj2756 [SCOI2012]奇怪的游戏

2017-08-15 17:21 183 查看

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

正解:二分+最大流。

这道题还是很不容易想啊,不过我写了个骗分居然有$90$分。。

首先我们肯定是要黑白染色,相邻的点染不同的颜色。

然后我们按照格子数分奇偶讨论:

如果格子数为偶数,那么黑点和白点个数相同,于是所有黑点的和与所有白点的和必须相同,否则一定不合法。

因为当黑点和白点个数相同时,每个点的权值$+1$,等价于将相邻的上下两点匹配一次,所以我们发现每个点的最终权值是满足单调性的。也就是说如果$v$合法,那么$v+1$也一定合法,于是我们直接二分最终的权值,建图跑最大流就行了。

当格子数为奇数时,黑点与白点个数不同。那么我们可以直接算出最终每个格子的权值。

由$v*num_{black}-sum_{black}=v*num_{white}-sum_{white}$,解得$v=(sum_{black}-sum_{while})/(num_{black}-num_{white})$。

$num$为格子数量,$sum$为初始的格子权值和。

那么我们判断一下$v$是否合法就行了。

#include <bits/stdc++.h>
#define il inline
#define RG register
#define ll long long
#define inf (1LL<<60)
#define N (1000010)
#define pos(i,j) ((i-1)*m+j)

using namespace std;

struct edge{ int nt,to; ll flow,cap; }g[N<<1];

int head
,d
,q
,a[42][42],S,T,n,m,num,tot;
ll x,y,ans;

il int gi(){
RG int x=0,q=1; RG char ch=getchar();
while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
return q*x;
}

il void insert(RG int from,RG int to,RG ll cap){
g[++num]=(edge){head[from],to,0,cap},head[from]=num; return;
}

il int bfs(RG int S,RG int T){
for (RG int i=1;i<=T;++i) d[i]=0;
RG int h=0,t=1; q[t]=S,d[S]=1;
while (h<t){
RG int x=q[++h],v;
for (RG int i=head[x];i;i=g[i].nt){
v=g[i].to;
if (!d[v] && g[i].cap>g[i].flow){
d[v]=d[x]+1,q[++t]=v;
if (v==T) return 1;
}
}
}
return d[T];
}

il ll dfs(RG int x,RG int T,RG ll a){
if (!a || x==T) return a; RG ll flow=0,f; RG int v;
for (RG int i=head[x];i;i=g[i].nt){
v=g[i].to;
if (d[v]==d[x]+1 && g[i].cap>g[i].flow){
f=dfs(v,T,min(a,g[i].cap-g[i].flow));
if (!f){ d[v]=0; continue; }
g[i].flow+=f,g[i^1].flow-=f;
flow+=f,a-=f; if (!a) return flow;
}
}
return flow;
}

il ll maxflow(RG int S,RG int T){
RG ll flow=0;
while (bfs(S,T)) flow+=dfs(S,T,inf);
return flow;
}

il int check(RG ll key){
for (RG int i=1;i<=T;++i) head[i]=0; num=1;
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=m;++j){
if ((i+j)&1) insert(S,pos(i,j),key-a[i][j]),insert(pos(i,j),S,0);
else insert(pos(i,j),T,key-a[i][j]),insert(T,pos(i,j),0);
}
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=m;++j)
if ((i+j)&1){
if (i>1) insert(pos(i,j),pos(i-1,j),inf),insert(pos(i-1,j),pos(i,j),0);
if (i<n) insert(pos(i,j),pos(i+1,j),inf),insert(pos(i+1,j),pos(i,j),0);
if (j>1) insert(pos(i,j),pos(i,j-1),inf),insert(pos(i,j-1),pos(i,j),0);
if (j<m) insert(pos(i,j),pos(i,j+1),inf),insert(pos(i,j+1),pos(i,j),0);
}
maxflow(S,T);
for (RG int i=1,k=2;i<=n;++i)
for (RG int j=1;j<=m;++j,k+=2)
if (g[k].flow!=g[k].cap) return 0;
return 1;
}

il void work(){
n=gi(),m=gi(),tot=x=y=0,S=n*m+1,T=S+1;
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=m;++j){
a[i][j]=gi(),tot=max(tot,a[i][j]);
if ((i+j)&1) x+=a[i][j]; else y+=a[i][j];
}
if (n*m%2==0){
if (x!=y){ puts("-1"); return; }
RG ll l=tot,r=1LL<<50,mid;
while (l<=r){
mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1; else l=mid+1;
}
printf("%lld\n",(ans*n*m>>1)-x);
} else{
RG ll X=y-x; if (X<tot){ puts("-1"); return; }
if (check(X)) printf("%lld\n",X*((n*m)>>1)-x);
else puts("-1");
}
return;
}

int main(){
#ifndef ONLINE_JUDGE
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
#endif
RG int T=gi();
while (T--) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: