給定無向圖 N 點 M 邊,初始所有點存活。依次摧毀 Q 個點(每次摧毀一個),求每次摧毀後的連通塊數(被摧毀點不計入連通塊)。N, M, Q ≤ 2×10^5。
destroy[1..Q];destroyed[x] = true;now = N - Q;Q 遞減到 1,「恢復」點 x = destroy[i]:
destroyed[x] = false,now++(新增孤立點);x 的所有鄰邊,若鄰居未被摧毀,Unite(x, neighbor),合併成功 now--;now 為第 i-1 次摧毀後的答案(即第 i 次摧毀前的狀態)。#include <bits/stdc++.h>
using namespace std;
const int MAXM = 400400;
struct Edge { int to, next; } e[MAXM];
int head[MAXM], tot;
int fa[MAXM], stk[MAXM], destroy[MAXM], top, now;
bool destroyed[MAXM];
int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); }
void Unite(int x, int y) {
int fx = Find(x), fy = Find(y);
if (fx == fy) return;
--now; fa[fy] = fx;
}
void AddEdge(int x, int y) {
e[++tot] = {y, head[x]}; head[x] = tot;
e[++tot] = {x, head[y]}; head[y] = tot;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m; cin >> n >> m;
for (int i = 0, u, v; i < m; ++i) {
cin >> u >> v; ++u; ++v; // 1-based
AddEdge(u, v);
}
int q; cin >> q;
for (int i = 1; i <= q; ++i) {
cin >> destroy[i]; ++destroy[i];
destroyed[destroy[i]] = true;
}
now = n - q;
for (int i = 1; i <= n; ++i) if (!destroyed[i])
for (int j = head[i]; j; j = e[j].next)
if (!destroyed[e[j].to]) Unite(i, e[j].to);
stk[++top] = now; // 所有摧毀完成後的狀態
for (int i = q; i >= 1; --i) {
int x = destroy[i];
destroyed[x] = false;
++now; // 恢復孤立點
for (int j = head[x]; j; j = e[j].next)
if (!destroyed[e[j].to]) Unite(x, e[j].to);
stk[++top] = now;
}
while (top) cout << stk[top--] << '\n';
return 0;
}
++u 轉 1-based,避免 0 索引混淆;now 初始為 存活點數(N-Q),每次 Unite 成功遞減;++now 再合併邊,邏輯清晰;stk 逆序存儲,最後彈出正序輸出;rank 或 size 優化。O((N+M+Q) α(N)),近似線性;O(N+M+Q)。sum[fa];Find(u)==Find(v) 時刻。「逆向思維 + 並查集增量構建」是處理「刪除操作難、添加操作易」動態圖問題的萬金油模板,必須熟練掌握。