1. 程式人生 > >P5038 [SCOI2012]奇怪的遊戲 二分+網絡流

P5038 [SCOI2012]奇怪的遊戲 二分+網絡流

con empty 中間 黑點 char 根據 spa 接下來 lse

$ \color{#0066ff}{ 題目描述 }$

Blinker最近喜歡上一個奇怪的遊戲。

這個遊戲在一個 \(N \times M\) 的棋盤上玩,每個格子有一個數。每次\(Blinker\)會選擇兩個相鄰的格子,並使這兩個數都加上\(1\)

現在\(Blinker\)想知道最少多少次能使棋盤上的數都變成同一個數,如果永遠不能變成同一個數則輸出\(-1\)

\(\color{#0066ff}{輸入格式}\)

輸入的第一行是一個整數\(T\),表示輸入數據有T輪遊戲組成。

每輪遊戲的第一行有兩個整數\(N\)\(M\), 分別代表棋盤的行數和列數。 接下來有\(N\)行,每行\(M\)個數。

\(\color{#0066ff}{輸出格式}\)

對於每個遊戲輸出最少能使遊戲結束的次數,如果永遠不能變成同一個數則輸出\(-1\)

\(\color{#0066ff}{輸入樣例}\)

2 
2 2 
1 2 
2 3 
3 3 
1 2 3 
2 3 4 
4 3 2 

\(\color{#0066ff}{輸出樣例}\)

2 
-1 

\(\color{#0066ff}{數據範圍與提示}\)

對於\(30%\)的數據,保證\(T<=10,1<=N,M<=8\)

對於\(100%\)的數據,保證 \(T<=10,1<=N,M<=40\),所有數為正整數且小於\(1000000000\)

\(\color{#0066ff}{題解}\)

對於這種方格圖,而且還是相鄰的數操作,顯然可以考慮黑白染色!

可以發現,每次操作只會同時修改兩個顏色不同的位置(顯然。。。)

既然最後所有的數相同,不妨假設那個數是x,那麽再結合黑白染色,可以得出一個式子(有解的情況下)

\(黑點數量*x-黑點權值和=白點數量*x-白點權值和\),其實就是操作次數相同的等式

觀察一下這個式子。。。嗯????xTM能解出來我去,不會就這樣水了吧。。。

當然不會,x能解出來當且僅當黑白點數量不同(除數不為0),這樣我們就能特判一下得到x

當然x可能不合法,要判一下,判斷的方法下面會說

那麽接下來就是黑白點相同的情況了

根據上面的式子,這時候如果兩個權值和不等,那顯然無解

那如果相等呢??如何求解x?

考慮二分答案,但是我們要判斷有沒有單調性

顯然當前的圖肯定是一個有偶數點個數的圖,如果當前x成立,顯然由於圖是偶數個點,x+1一定是成立的

反之同理,於是這是有單調性的

然後,二分完x,我們如何判斷x是否合法(都黑白染色了當然是網絡流)

起點向每個白點連x-權值的邊,代表需要幾次操作,中間連inf的邊,右邊同理

當前方案合法當且僅當最大流可以滿足所有點, 這也是上面判斷x是否合法的方式

#include<bits/stdc++.h>
#define LL long long
LL read() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
template<class T> bool chkmax(T &a, const T &b) { return a < b? a = b, 1 : 0; }
template<class T> bool chkmin(T &a, const T &b) { return b < a? a = b, 1 : 0; }
const int maxn = 5e5 + 10;
const LL inf = 9999999999999LL;
struct node {
    LL to, can;
    node *nxt, *rev;
    node(LL to = 0, LL can = 0, node *nxt = NULL): to(to), can(can), nxt(nxt) { rev = NULL; }
}pool[maxn], *tail, *head[maxn], *cur[maxn];
int dep[maxn], n, m, s, t, mp[66][66];
int rx[] = {1, -1, 0, 0};
int ry[] = {0, 0, 1, -1};
void add(int from, int to, LL can) { head[from] = new(tail++) node(to, can, head[from]); }
void link(int from, int to, LL can) {
    add(from, to, can), add(to, from, 0LL);
    head[from]->rev = head[to]; head[to]->rev = head[from];
}
bool bfs() {
    for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
    std::queue<int> q; 
    q.push(s); dep[s] = 1;
    while(!q.empty()) {
        int tp = q.front(); q.pop();
        for(node *i = head[tp]; i; i = i->nxt) 
            if(!dep[i->to] && i->can)
                dep[i->to] = dep[tp] + 1, q.push(i->to); 
    }
    return dep[t];
}
LL dfs(int x, LL change) {
    if(x == t || !change) return change;
    LL flow = 0, ls;
    for(node *&i = cur[x]; i; i = i->nxt)
        if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(i->can, change)))) {
            flow += ls;
            change -= ls;
            i->can -= ls;
            i->rev->can += ls;
            if(!change) break;
        }
    return flow;
}
LL dinic() {
    LL flow = 0;
    while(bfs()) flow += dfs(s, inf);
    return flow;
}
int id(int x, int y) { return (x - 1) * m + y; }
bool ok(LL mid) {
    tail = pool;
    s = 0, t = n * m + 1;
    for(int i = s; i <= t; i++) head[i] = NULL;
    LL now = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            if((i + j) & 1) {
                now += mid - mp[i][j], link(s, id(i, j), mid - mp[i][j]);
                for(int k = 0; k < 4; k++) {
                    int xx = i + rx[k];
                    int yy = j + ry[k];
                    if(xx >= 1 && xx <= n && yy >= 1 && yy <= m) link(id(i, j), id(xx, yy), inf);
                }
            }
            else link(id(i, j), t, mid - mp[i][j]);
        }
    return now == dinic();
}

int main() {
    for(int T = read(); T --> 0;) {
        n = read(), m = read();
        int maxval = 0;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                chkmax(maxval, mp[i][j] = read());
        LL tot0 = 0, tot1 = 0, num0 = 0, num1 = 0;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++) {
                if((i + j) & 1) num1++, tot1 += mp[i][j];
                else num0++, tot0 += mp[i][j];
            }
        if(num0 ^ num1) {
            LL endval = (tot0 - tot1) / (num0 - num1);
            if(endval >= maxval && ok(endval)) printf("%lld\n", endval * num1 - tot1);
            else puts("-1");
        }
        else {
            if(tot0 ^ tot1) puts("-1");
            else {
                LL l = maxval, r = inf >> 1, ans = r;
                while(l <= r) {
                    LL mid = (l + r) >> 1;
                    if(ok(mid)) ans = mid, r = mid - 1;
                    else l = mid + 1;
                }
                printf("%lld\n", ans == inf >> 1? -1 : ans * num1 - tot1);
            }
        }
    }
    return 0;
}

P5038 [SCOI2012]奇怪的遊戲 二分+網絡流