題意:
n*m的白格子,每次將一些部分塗黑,求剩下幾個白格連通塊
思路:
假設是順序塗黑,然後問黑格子連通塊有多少個,那就是並查集裸題。
本題中順序考慮很難。考慮逆向,假設全部塗完了,對每個白格子再往周圍遍歷一次,用並查集將相鄰白格子連起來,Union操作成功一次,連通塊減少一個。就可以算出此時的白色連通塊。
然後刪除一個黑格子,實際上多了個白格子,算作多了個連通塊。然後再用這個白格子遍歷周圍白格子,進行Union操作,Union成功了連通塊減少一個。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 10005;
vector<pair<int,int> >G[maxn];
vector<int>res;
int a[maxn][maxn],fa[1000005];
int dirx[] = {1,-1,0,0};
int diry[] = {0,0,1,-1};
int n,m,q;
int f(int x,int y) {
return (x - 1) * m + y;
}
bool check(int x,int y) {
if(x >= 1 && y >= 1 && x <= n && y <= m) return true;
return false;
}
int findset(int x) {
if(fa[x] == x) return x;
return fa[x] = findset(fa[x]);
}
bool Union(int x,int y) {
int rx = findset(x),ry = findset(y);
if(rx != ry) {
fa[rx] = ry;
return true;
}
return false;
}
int main() {
scanf("%d%d%d",&n,&m,&q);
int ans = n * m;
for(int i = 1;i <= n * m;i++) {
fa[i] = i;
}
for(int i = 1;i <= q;i++) {
int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(x1 == x2) {
for(int j = y1;j <= y2;j++) {
if(a[x1][j]) continue;
a[x1][j] = 1;
ans--;
G[i].push_back({x1,j});
}
}
else {
for(int j = x1;j <= x2;j++) {
if(a[j][y1]) continue;
a[j][y1] = 1;
ans--;
G[i].push_back({j,y1});
}
}
}
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= m;j++) {
if(!a[i][j]) {
for(int d = 0;d < 4;d++) {
int dx = dirx[d] + i;
int dy = diry[d] + j;
if(check(dx,dy) && (!a[dx][dy] && Union(f(i,j),f(dx,dy)))) {
ans--;
}
}
}
}
}
for(int i = q;i >= 1;i--) {
res.push_back(ans);
for(int j = 0;j < G[i].size();j++) {
int x = G[i][j].first,y = G[i][j].second;
a[x][y] = 0;ans++;
for(int d = 0;d < 4;d++) {
int dx = x + dirx[d],dy = y + diry[d];
if(check(dx,dy) && (!a[dx][dy] && Union(f(x,y),f(dx,dy)))) {
ans--;
}
}
}
}
for(int i = res.size() - 1;i >= 0;i--) {
printf("%d\n",res[i]);
}
return 0;
}