BZOJ2756:[SCOI2012]奇怪的游戏(最大流,二分)
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
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
2
-1
-1
HINT
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
Solution
emmm这个题真的有毒啊……INF改成8e8才过……最慢的点跑2s……幸亏BZOJ算总时间
首先看到这个数据范围十有八九网络流没跑了……
先将格子黑白染色,考虑一通操作成功后每个格子的数都是v,若:
1、n*m是奇数。设黑白格子个数为num[0/1],和为sum[0/1],那么$num0*x-sum0=num1*x-sum1$,因为每次操作肯定会给一个黑点和一个白点加。
2、n*m是偶数。如果最终答案是v满足的话,那么显然最终答案是v+1肯定也是可以满足的。这个就可以二分了。
现在的问题转换为判断是否可行。
s-black,容量为v-a[i][j]
white-e,容量为v-a[i][j]
black-white, 容量为INF。
怎么理解呢?把INF边看成给两端的数各自加一,而INF边连着的两端的边又限制了INF边的使用次数。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 #define N (2001) 6 #define INF (8000000000LL) 7 #define LL long long 8 using namespace std; 9 10 struct Edge{LL to,next,flow;}edge[N<<4]; 11 LL T,n,m,s,e=1999,Depth[N],num[2],sum[2],maxn,cnt; 12 LL head[N],num_edge,a[N][N],col[N][N],id[N][N]; 13 LL dx[6]={0,1,-1,0,0},dy[6]={0,0,0,1,-1}; 14 queue<LL>q; 15 16 void add(LL u,LL v,LL l) 17 { 18 edge[++num_edge].to=v; 19 edge[num_edge].next=head[u]; 20 edge[num_edge].flow=l; 21 head[u]=num_edge; 22 } 23 24 LL Dfs(LL x,LL low) 25 { 26 if (x==e || low==0) return low; 27 LL f=0,Min=0; 28 for (int i=head[x]; i; i=edge[i].next) 29 if (edge[i].flow && Depth[edge[i].to]==Depth[x]+1 && (Min=Dfs(edge[i].to,min(low,edge[i].flow)))) 30 { 31 edge[i].flow-=Min; 32 edge[((i-1)^1)+1].flow+=Min; 33 f+=Min; low-=Min; 34 if (!low) break; 35 } 36 if (!f) Depth[x]=-1; 37 return f; 38 } 39 40 bool Bfs(LL s,LL e) 41 { 42 memset(Depth,0,sizeof(Depth)); 43 Depth[s]=1; q.push(s); 44 while (!q.empty()) 45 { 46 LL x=q.front(); q.pop(); 47 for (int i=head[x]; i; i=edge[i].next) 48 if (!Depth[edge[i].to] && edge[i].flow) 49 { 50 Depth[edge[i].to]=Depth[x]+1; 51 q.push(edge[i].to); 52 } 53 } 54 return (Depth[e]!=0); 55 } 56 57 bool Dinic(LL s,LL e) 58 { 59 while (Bfs(s,e)) Dfs(s,INF); 60 for (int i=head[s]; i; i=edge[i].next) 61 if (edge[i].flow!=0) return false; 62 for (int i=head[e]; i; i=edge[i].next) 63 if (edge[((i-1)^1)+1].flow!=0) return false; 64 return true; 65 } 66 67 bool check(LL v) 68 { 69 memset(head,0,sizeof(head)); num_edge=0; 70 memset(edge,0,sizeof(edge)); 71 for (int i=1; i<=n; ++i) 72 for (int j=1; j<=m; ++j) 73 { 74 if (col[i][j]) add(s,id[i][j],v-a[i][j]),add(id[i][j],s,0); 75 else add(id[i][j],e,v-a[i][j]),add(e,id[i][j],0); 76 if (!col[i][j]) continue; 77 for (int k=1; k<=4; ++k) 78 { 79 LL x=i+dx[k], y=j+dy[k]; 80 if (x<1 || x>n || y<1 || y>m) continue; 81 add(id[i][j],id[x][y],INF),add(id[x][y],id[i][j],0); 82 } 83 } 84 return Dinic(s,e); 85 } 86 87 int main() 88 { 89 scanf("%lld",&T); 90 while (T--) 91 { 92 scanf("%lld%lld",&n,&m); 93 sum[0]=sum[1]=maxn=cnt=0; 94 if (n*m%2) num[1]=n*m/2+1,num[0]=n*m/2; 95 else num[1]=num[0]=n*m/2; 96 for (int i=1; i<=n; ++i) 97 for (int j=1; j<=m; ++j) 98 { 99 scanf("%lld",&a[i][j]); 100 id[i][j]=++cnt; 101 maxn=max(maxn,a[i][j]); 102 if (i%2==j%2) col[i][j]=1; 103 sum[col[i][j]]+=a[i][j]; 104 } 105 if (n*m%2) 106 { 107 LL v=(sum[1]-sum[0])/(num[1]-num[0]); 108 if (check(v)) printf("%lld\n",(v*n*m-sum[0]-sum[1])/2); 109 else printf("-1\n"); 110 } 111 else 112 { 113 LL l=maxn, r=INF, ans=-1; 114 while (l<=r) 115 { 116 LL mid=(l+r)/2; 117 if (check(mid)) r=mid-1,ans=mid; 118 else l=mid+1; 119 } 120 if (ans==-1) printf("-1\n"); 121 else printf("%lld\n",(ans*n*m-sum[0]-sum[1])/2); 122 } 123 } 124 }