1. 程式人生 > >[NOI 2016]網格

[NOI 2016]網格

bound pac size i++ www else 並不是 一個點 情況

Description

題庫鏈接

給出一張 \(n\times m\) 的網格,在其中刪去 \(c\) 個格子,問至少再刪去幾個能使得圖上存在兩點不連通,或輸出無解。

多組詢問,詢問組數 \(T\)

\(1\leq T\leq 20,1\leq n,m\leq 10^9,\sum c\leq 10^5\)

Solution

觀察題目,容易發現答案只會在 \(-1,0,1,2\) 之間,那麽就可以隨機輸出其中一個數,以 \(\frac{}{4^T}\) 的概率通過這道題

那麽分類討論。

  • 如果答案為 \(-1\) ,顯然只有兩種情況:只有剩一個格子,或者只有兩個格子且兩個格子相鄰。這些是可以直接特判的。
  • 如果答案為 \(0\) ,顯然是原圖已經出現了不連通的狀況。
  • 若果答案為 \(1\) ,那麽原圖存在一個點,使得刪去這個點後圖不聯通,即原圖存在割點。
  • 否則答案為 \(2\)

由於坐標過大,我們不能直接在原圖上求解,考慮離散。其實註意到網格上留有的格子有用的只是與每個刪掉的格子八連通的格子。

我們可以把這些格子留下來建圖。不過有一種特殊的情況,那就是邊界問題,舉一個例子

.....
...00
...X1
...00
.....

如題原本是一個 \(5\times 5\) 的格子,在 \((3,5)\) 被刪掉了一個格子,如果我們只考慮八連通,那麽就會出現 \(\text{X}\) 是一個割點,然而在原圖中並不是,所以就考慮將一個被刪除的點的周圍 \(5\times 5\)

的格子(共 \(24\) 個)都拿下來建新圖。這樣點數是 \(O(24\sum c)\) 的。

特判掉 \(-1,0\) 之後,就跑一遍 \(\text{tarjan}\) 判是否有割點即可。

Code

我用了 \(\text{map}\) ,在 \(\text{UOJ}\) 上過不去...( \(\text{bzoj}\) 過了就茍且偷生...233333...

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 100000+5;
const int w1[8] = {1, -1, 0, 0, 1, 1, -1, -1};
const int w2[8] = {0, 0, 1, -1, -1, 1, -1, 1};
void gi(int &x) {
    char ch = getchar(); x = 0;
    for (; ch < '0' || ch > '9'; ch = getchar());
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x<<1)+(x<<3)+ch-48;
}

int n, m, c, u, v, idx, color[N<<3], cnt;
struct pii {
    int first, second;
    pii (int _first = 0, int _second = 0) {first = _first, second = _second; }
    bool operator < (const pii &b) const {
        return first == b.first ? second < b.second : first < b.first;
    }
    bool operator == (const pii &b) const {return first == b.first && second == b.second; }
};
pii a[N], t, t1;
map<pii, int>id, mp;
queue<pii>Q, P;
struct tt {int to, next; }edge[N<<6];
int path[N<<3], top, rk[N<<3], iscut[N<<3], dfn[N<<3], low[N<<3], times;

bool exist(int u, int v) {
    t = a[lower_bound(a+1, a+c+1, pii(u, v))-a];
    return t.first == u && t.second == v;
}
bool outof(int x, int y, int u, int d, int l, int r) {
    return x < u || x > d || y < l || y > r;
}
void add(int u, int v) {edge[++top] = (tt){v, path[u]}; path[u] = top; }
bool judge() {
    if (1ll*n*m-c <= 1) return false;
    if (1ll*n*m-c >= 3) return true;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (!exist(i, j)) {
                for (int k = 0; k < 4; k++) {
                    if (outof(i+w1[k], j+w2[k], 1, n, 1, m)) continue;
                    if (!exist(i+w1[k], j+w2[k])) return false;
                }
                return true;
            }
        }
}
void bfs(pii t) {
    P.push(t); int u, v, u1, v1;
    while (!P.empty()) {
        t = P.front(); P.pop(); u = t.first, v = t.second; int c = color[id[t]];
        for (int k = 0; k < 4; k++)
            if (!outof(u1 = u+w1[k], v1 = v+w2[k], 1, n, 1, m) && !exist(u1, v1) && id.count(t1 = pii(u1, v1))) {
                if (color[id[t1]]) continue;
                color[id[t1]] = c; P.push(t1);
            }
    }
}
bool connect() {
    while (!Q.empty()) {
        t = Q.front(); Q.pop();
        if (color[id[t]]) continue;
        else color[id[t]] = ++cnt, bfs(t);
    }
    for (int Id = 1; Id <= c; Id++) {
        int u = a[Id].first, v = a[Id].second, c = -1;
        for (int i = -2; i <= 2; i++)
            for (int j = -2; j <= 2; j++) {
                if (outof(u+i, v+j, 1, n, 1, m) || exist(u+i, v+j)) continue;
                t = pii(u+i, v+j);
                if (c == -1) c = color[id[t]];
                else if (c != color[id[t]]) return false;
            }
    }
    return true;
}
void build(int u, int v) {
    for (int i = -2; i <= 2; i++)
        for (int j = -2; j <= 2; j++) {
            if (outof(u+i, v+j, 1, n, 1, m) || exist(u+i, v+j)) continue;
            t = pii(u+i, v+j);
            int x = id.count(t) ? id[t] : (Q.push(t), id[t] = ++idx);
            if (!outof(u+i+1, v+j, max(1, u-2), min(n, u+2), max(1, v-2), min(m, v+2)) && !exist(u+i+1, v+j)) {
                t = pii(u+i+1, v+j);
                int y = id.count(t) ? id[t] : (Q.push(t), id[t] = ++idx);
                if (x > y) {if (!mp.count(pii(y, x))) add(x, y); add(y, x); mp[pii(y, x)] = 1; }
                else {if (!mp.count(pii(x, y))) add(x, y); add(y, x); mp[pii(x, y)] = 1; }
            }
            if (!outof(u+i, v+j+1, max(1, u-2), min(n, u+2), max(1, v-2), min(m, v+2)) && !exist(u+i, v+j+1)) {
                t = pii(u+i, v+j+1);
                int y = id.count(t) ? id[t] : (Q.push(t), id[t] = ++idx);
                if (x > y) {if (!mp.count(pii(y, x))) add(x, y); add(y, x); mp[pii(y, x)] = 1; }
                else {if (!mp.count(pii(x, y))) add(x, y); add(y, x); mp[pii(x, y)] = 1; }
            }
            if (abs(i) <= 1 && abs(j) <= 1) rk[x] = 1;
        }
}
void tarjan(int u, int fa) {
    iscut[u] = 0; dfn[u] = low[u] = ++times; int sz = 0, v;
    for (int i = path[u]; i; i = edge[i].next) {
        if ((v = edge[i].to) == fa) continue;
        if (!dfn[v]) {
            ++sz; tarjan(v, u);
            if (low[v] >= dfn[u]) iscut[u] = 1;
            low[u] = min(low[u], low[v]);
        }else low[u] = min(low[u], dfn[v]);
    }
    if (fa == 0 && sz == 1) iscut[u] = 0;
}
void work() {
    memset(path, top = idx = 0, sizeof(path)); id.clear(), mp.clear();
    memset(dfn, times = 0, sizeof(dfn)); memset(rk, 0, sizeof(rk));
    memset(color, cnt = 0, sizeof(color));
    gi(n), gi(m), gi(c);
    for (int i = 1; i <= c; i++) {
        gi(u), gi(v); a[i] = pii(u, v);
    }
    sort(a+1, a+c+1); a[c+1] = pii(0, 0);
    if (!judge()) {puts("-1"); return; }
    for (int i = 1; i <= c; i++) build(a[i].first, a[i].second);
    if (!connect()) {puts("0"); return; }
    if (n == 1 || m == 1) {puts("1"); return; }
    for (int i = 1; i <= idx; i++) {
        if (!dfn[i]) tarjan(i, 0);
        if (rk[i] && iscut[i]) {puts("1"); return; }
    }
    puts("2");
}
int main() {
    int t; gi(t); while (t--) work();
    return 0;
}

[NOI 2016]網格