跳转至

Y31A 沧海桑田

题目大意

有一张 \(50\times 10^5\) 的网格图,初始时所有位置都是海洋。你需要维护两种操作:

  • 给定一个矩形,将矩形内所有位置更改为陆地;
  • 给定两个点,询问它们之间的陆地是否连通;

注意到没有将陆地改变为海洋的操作,因此海洋变为陆地的总次数是均摊 \(O(nm)\) 的,因此可以使用并查集维护陆地连通块。

考虑如何找到矩形内剩余海洋。我们维护 \(f_{x,y}\) 表示第 \(x\)\(y\) 列位置,向右第一个海洋的下标。如果 \((x,y)\) 就是海洋,则 \(f_{x,y}=y\);其余情况 \(f_{x,y}=f_{x,y+1}\)

\((x,y)\) 位置从海洋修改为陆地等价于将 \(f_x\) 中所有 \(y\) 修改为 \(f_{x,y+1}\)。注意到这正是一种染色问题,仍然可以使用并查集维护。

代码
#include<iostream>
using namespace std;
const int N = 1E5 + 10;
const int M = 55;

int change(int x, int y) {
    return x * N + y;
}

int t;
int col[N * M];

int nxt[N * M];
int findNext(int x) {
    if(nxt[x] == x) return x;
    return nxt[x] = findNext(nxt[x]);
}

int fa[N * M];
int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

void merge(int x, int y) {
    fa[find(x)] = find(y);
}

bool in_bound(int x) {
    return 1 <= x && x < N * M;
}

void update(int x) {
    col[x] = 1;
    if(in_bound(x + 1) && col[x + 1]) merge(x, x + 1);
    if(in_bound(x - 1) && col[x - 1]) merge(x, x - 1);
    if(in_bound(x + N) && col[x + N]) merge(x, x + N);
    if(in_bound(x - N) && col[x - N]) merge(x, x - N);
}

int main() {

    for(int i = 1; i < N * M; i++) {
        nxt[i] = i;
        fa[i] = i;
    }

    cin >> t;
    while(t--) {
        int op, x1, y1, x2, y2;
        cin >> op >> x1 >> y1 >> x2 >> y2;
        if(op == 0) {
            if(x1 > x2) swap(x1, x2);
            if(y1 > y2) swap(y1, y2);
            for(int i = x1; i <= x2; i++) {
                int j = change(i, y1);
                while(j <= change(i, y2)) {
                    update(j);
                    if(nxt[j] == j) nxt[j] = j + 1;
                    j = findNext(j);
                }
            }
        } else {
            if(col[change(x1, y1)] && col[change(x2, y2)] && find(change(x1, y1)) == find(change(x2, y2))) {
                cout << 1 << endl;
            } else cout << 0 << endl;
        }
    }

    return 0;
}