N (≤ 100,000) 個村莊,M (≤ 100,000) 條路。Q (≤ M) 條路按給定順序被摧毀。每摧毀一條後,輸出當前連通塊(駐軍區)數量。
edges[i] = (u, v)。del[1..Q],標記 isDel[edgeId] = true。ans = N。!isDel[i],union(u, v)(合併時 ans--)。res[i] = ans; // 記錄加回第 i 條路**前**的連通塊數(即正序摧毀第 i 條**後**的數量)union(edges[del[i]].u, edges[del[i]].v);res[1..Q]。
#include
#include
const int MAX = 100005;
int fa[MAX], x[MAX], y[MAX], del[MAX], res[MAX];
bool isDel[MAX];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void unite(int a, int b) {
a = find(a); b = find(b);
if (a != b) { fa[b] = a; ans--; }
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
for (int i = 1; i <= m; ++i) scanf("%d%d", &x[i], &y[i]);
int q; scanf("%d", &q);
memset(isDel, 0, sizeof(isDel));
for (int i = 1; i <= q; ++i) { scanf("%d", &del[i]); isDel[del[i]] = true; }
for (int i = 1; i <= n; ++i) fa[i] = i;
int ans = n;
for (int i = 1; i <= m; ++i) if (!isDel[i]) unite(x[i], y[i]);
for (int i = q; i >= 1; --i) {
res[i] = ans;
unite(x[del[i]], y[del[i]]);
}
for (int i = 1; i < q; ++i) printf("%d ", res[i]);
printf("%d\n", res[q]);
}
return 0;
}
res[i] 存的是**加回第 i 條路前**的連通塊數,對應正序**摧毀第 i 條路後**的數量。CSUOJ 1601 是「離線動態連通性」的經典模板題:逆向思維 + 並查集。遇到「批量刪除邊/點後查詢連通性」類問題,首選逆向加邊離線並查集。