Comet OJ - Contest #13 C2 佛御石之鉢 -不碎的意志 -(並查集 + 技巧)

Comet OJ - Contest #13 C2

題意

給出一個 n 行 m 列的 01 矩陣。有 q 次操作,每次操作選取一個子矩陣,將子矩陣變爲全 1,每次操作後輸出當前連通塊個數,(上下左右算聯通)。
n, m < 1e4; q < 3e4.

分析

首先,只有發生 0->1 的格子,纔會導致連通塊數目變化。也就是說,對於每次操作只關注子矩陣裏原本是 0 的格子,(0 的格子會不斷減小)

當原本是 0 的格子變爲 1,連通塊數目 + 1,然後分別遍歷上下左右四個位置,如果遍歷到的位置爲 1,且之前沒有在一個連通塊,那麼現在就將他們併入一個集合,同時連通塊數目 - 1。

實現方面,可以在每一行開一個並查集,並查集的祖先表示右邊第一個 0 的位置(包括本身),這樣在遍歷子矩陣時,只需要一行一行的找 0 的位置即可。

而判斷兩個 1 位置是否在一個聯通塊,可以用一個並查集維護,做法是將每個二維點都打上標號。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define fi first
#define se second
const int INF = 0x3f3f3f3f;
const int N = 1e3 + 10;

int t, n, m, q, ans;
char arr[N][N];
int fa[N][N], dsu[N*N];
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

inline int find(int fa[], int x){   //在第 i 行並查集
    return fa[x] = fa[x] == x ? fa[x] : find(fa, fa[x]);
}

inline int id(int x, int y){    // 對二維點打標號
    return x * m + y;
}

void light(int x, int y){   // 像四個方向找 ‘1’,將兩個點併入集合
    arr[x][y] = '1';
    ans++;          // 0 -> 1,連通塊數目 + 1
    fa[x][y] = y + 1;   
    for (int i = 0; i < 4; i++){        // 四個方向
        int tx = x + dir[i][0], ty = y + dir[i][1];
        if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && arr[tx][ty] == '1' && find(dsu, id(x, y)) != find(dsu, id(tx, ty))){
            dsu[find(dsu, id(x, y))] = find(dsu, id(tx, ty));   // 如果兩個點不是同一個集合
            ans--;  // 併入同一個集合,聯通數目減一
        }
    }
}

int main(){
    scanf("%d%d", &n, &m);
    ans = 0;
    for (int i = 1; i <= n; i++){
        scanf("%s", arr[i] + 1);
        for (int j = 1; j <= m; j++){
            fa[i][j] = j, dsu[id(i, j)] = id(i, j);
        }
        fa[i][m + 1] = m + 1;
    }
    for (int i = 1; i <= n; i++){       
        for (int j = 1; j <= m; j++){
            if(arr[i][j] == '1')
                light(i, j);
        }
    }
    scanf("%d", &q);
    while(q--){
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        for (int i = a; i <= c; i++){       // 找每一行 0 的位置
            int s = find(fa[i], b);
            while(s <= d){
                light(i, s);
                s = find(fa[i], b);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

/*
5 6
101010
000001
101010
000001
101010
3
1 2 5 2
4 4 4 4
1 3 3 6
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章