bzoj2756: \[SCOI2012\]奇怪的游戏 二分法 网络流

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

Sample Output

2
-1

HINT

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

分析

算是比较巧妙的一道题把。
嘛,网格相邻这种东西肯定是黑白染色。
首先假设最终相同的数是 x
每次操作会对黑白块各产生一次贡献,于是列出式子
C n t w h i t e x S u m w h i t e = C n t b l a c k x S u m b l a c k
很快可以得到
x = S u m w h i t e S u m b l a c k C n t w h i t e C n t b l a c k
得到 x 之后用网络流验证一下满流即可。
可是如果 C n t w h i t e == C n t b l a c k 就麻烦了。
如果这时 S u m w h i t e S u m b l a c k ,一定无解。(通过式子就知道了)
否则的话,不难发现此时如果 x 可行,那么 x + 1 一定可行。因为只要放个大招像放多米诺骨牌(什么鬼比喻)那样吧每个格子+1就可以了。
上二分即可。
注意类似discuss里面给的变态数据。
它告诉我们即使 C n t w h i t e == C n t b l a c k , S u m w h i t e == S u m b l a c k 也会有无解的情况。所以要特判是否顶到上界。
其实二分上界我也不是很懂是多大来着,反正开long long是没问题的。
注意各种特判哟!

代码

#include<cstdio>
#include<algorithm>
typedef long long LL;
const int N = 2e3 + 10, M = 1e4 + 10; const LL inf = 1e18;
const int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
int g[N], h[N], q[N], pr[N], cr[N], to[M], nx[M], p[50][50], a[50][50], b[2], T, S, mx, tp, n, m; 
LL w[M], s[2];
void add(int u, int v, LL ww) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = ww;}
void adds(int u, int v, LL w) {add(u, v, w); add(v, u, 0);}
bool Bfs() {
    for(int i = 1;i <= T; ++i) h[i] = -1, g[i] = 0;
    ++g[++h[T]]; int L, R; q[L = R = 1] = T;
    for(int u = q[L]; L <= R; u = q[++L]) 
        for(int i = pr[u]; i; i = nx[i])
            if(!~h[to[i]]) ++g[h[q[++R] = to[i]] = h[u] + 1];
    return ~h[S];
}
LL Dfs(int u, LL minf) {
    if(u == T) return minf; LL flow = 0, f;
    for(int i = cr[u]; i; i = nx[i]) 
    if(w[i] && h[u] == h[to[i]] + 1){
        f = Dfs(to[i], std::min(minf - flow, w[i]));
        w[i] -= f; w[i ^ 1] += f; w[i] ? cr[u] = i : 0; flow += f;
        if(flow == minf) return flow;
    }
    if(!--g[h[u]]) h[S] = T + 2;
    ++g[++h[u]]; cr[u] = pr[u];
    return flow;
}
LL Sap() {
    if(!Bfs()) return 0; LL ans = 0;
    for(int i = 1;i <= T; ++i) cr[i] = pr[i];
    for(;h[S] < T + 2;) ans += Dfs(S, inf);
    return ans;
}
bool Ck(LL x) {
    if(x < mx) return false;
    for(int i = 1; i <= T; ++i) pr[i] = 0; tp = 1;
    LL ans = 0;
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= m; ++j) {
            if(i + j & 1) {
                adds(S, p[i][j], x - a[i][j]), ans += x - a[i][j];
                for(int k = 0; k < 4; ++k) {
                    int x = i + dx[k], y = j + dy[k];
                    if(x && x <= n && y && y <= m) adds(p[i][j], p[x][y], inf);
                }
            }
            else adds(p[i][j], T, x - a[i][j]);
        }
    return Sap() == ans;
}
int main() {
    int C; scanf("%d", &C);
    for(;C--;) {
        scanf("%d%d", &n, &m); mx = 0; S = n * m + 1; T = S + 1; tp = 0; s[0] = s[1] = b[0] = b[1] = 0;
        for(int i = 1;i <= n; ++i)
            for(int j = 1;j <= m; ++j) {
                scanf("%d", &a[i][j]); p[i][j] = ++tp;
                s[i + j & 1] += a[i][j]; ++b[i + j & 1];
                mx = std::max(a[i][j], mx);
            }
        if(b[0] != b[1]) {
            LL x = s[0] - s[1] / (b[0] - b[1]);
            !Ck(x) ? puts("-1") : printf("%lld\n", x * b[0] - s[0]);
        }
        else {
            if(s[0] != s[1]) puts("-1");
            else {
                LL l = mx, r = 1e15;
                for(LL m;l != r; Ck(m = l + r >> 1) ? r = m : l = m + 1) ;
                if(l == 1e14) puts("-1");
                else printf("%lld\n", l * b[0] - s[0]);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/81415224