BZOJ2756【SCOI2012】奇怪的游戏 <二分答案+网络流>

Problem

【SCOI2012】奇怪的游戏

Time  Limit:  40  Sec\mathrm{Time\;Limit:\;40\;Sec}
Memory  Limit:  128  MB\mathrm{Memory\;Limit:\;128\;MB}

Description

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

Input

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

Output

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

Sample Input

1
2
3
4
5
6
7
8
2 
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2

Sample Output

1
2
2
-1

HINT

对于30%30\%的数据,保证T10,  1N,M8T\le10,\;1\le N,M\le8
对于100%100\%的数据,保证T10,  1N,M40T\le10,\;1\le N,M\le40,所有数为正整数且小于10910^9

标签:网络流 黑白染色 二分答案

Solution

黑白染色,两色个数为c1c_1c2c_2,两色初始数字和为s1s_1s2s_2。则有

c1×xs1=c2×xs2(c1c2)×x=s1s2c_1\times x-s_1=c_2\times x-s_2\\ (c_1-c_2)\times x=s_1-s_2\\

c1c2c_1\ne c_2时,可以直接解出xx,这时网络流checkcheck一下是否可能达到即可。
c1=c2c_1=c_2时,每次操作都会使一定能在某一基础上将所有格子都+1+1,那么所有大于等于最小xx的值都可以达到,具有二分性。那么二分xx,网络流checkcheck即可。

对于网络流checkcheck,建图如下:

  • 对于白格ii,连接Si  [cap=xvali]S\to i\;[cap=x-val_i]
  • 对于黑格ii,连接iT  [cap=xvali]i\to T\;[cap=x-val_i]
  • 对于每组相邻点x,yx,y,连接xy  [cap=]x\to y\;[cap=\infty]

再计算一个tot=所有白格的值与x的差之和tot=所有白格的值与x的差之和
最大流=tot最大流=tot,则当前xx可行。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
#define MAX_N 2000
#define MAX_M 20000
#define INF (1LL<<50)
#define mid ((l+r)>>1)
using namespace std;
typedef long long lnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int nxt[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int n, m, s, t, cnt, d[MAX_N+5], pr[MAX_N+5], cr[MAX_N+5];
struct node {int v, nxt; lnt c;} E[MAX_M+5];
int col[50][50], c0, c1; lnt a[50][50], s0, s1, mx;
int p(int x, int y) {return x*m-m+y;}
void init() {s = 0, t = n*m+1, cnt = 0, memset(pr, -1, sizeof pr);}
void insert(int u, int v, lnt c) {E[cnt] = (node){v, pr[u], c}, pr[u] = cnt++;}
void addedge(int u, int v, lnt c) {insert(u, v, c), insert(v, u, 0);}
bool BFS() {
queue <int> que; que.push(s);
memset(d, -1, sizeof d), d[s] = 0;
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v; lnt c = E[i].c;
if (~d[v] || !c) continue;
d[v] = d[u]+1, que.push(v);
}
}
return ~d[t];
}
lnt DFS(int u, lnt flow) {
if (u == t) return flow; lnt ret = 0;
for (int &i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v; lnt c = E[i].c;
if (d[u]+1 != d[v] || !c) continue;
lnt tmp = DFS(v, min(flow, c));
E[i].c -= tmp, E[i^1].c += tmp;
flow -= tmp, ret += tmp;
if (!flow) break;
}
if (!ret) d[u] = -1; return ret;
}
void cpy() {for (int i = s; i <= t; i++) cr[i] = pr[i];}
void rec() {for (int i = s; i <= t; i++) pr[i] = cr[i];}
lnt Dinic() {lnt ret = 0; cpy(); while (BFS()) ret += DFS(s, INF), rec(); return ret;}
bool chk(lnt x) {
init(); lnt tot = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) if (col[i][j])
addedge(s, p(i, j), x-a[i][j]), tot += x-a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) if (!col[i][j])
addedge(p(i, j), t, x-a[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) if (col[i][j])
for (int k = 0; k < 4; k++) {
int x = i+nxt[k][0], y = j+nxt[k][1];
if (x < 1 || x > n || y < 1 || y > m) continue;
addedge(p(i, j), p(x, y), INF);
}
return Dinic() == tot;
}
lnt bi_search(lnt l, lnt r) {
lnt ret = -1;
while (l <= r)
if (!chk(mid)) l = mid+1;
else ret = mid, r = mid-1;
return ret;
}
void color() {
mx = c0 = c1 = s0 = s1 = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
col[i][j] = (i+j)&1,
(col[i][j] ? c1++ : c0++);
}
int main() {
int T; read(T);
while (T--) {
read(n), read(m), color();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
read(a[i][j]), mx = max(mx, a[i][j]),
(col[i][j] ? s1 += a[i][j] : s0 += a[i][j]);
if (c0^c1) {
if ((s0-s1)/(c0-c1) >= m && chk((s0-s1)/(c0-c1)))
printf("%lld\n", (s0-s1)/(c0-c1)*c0-s0);
else puts("-1");
} else {
if (s0^s1) puts("-1");
else printf("%lld\n", bi_search(mx, INF)*c0-s0);
}
}
return 0;
}