1. 程式人生 > >盟國 (並查集的刪除操作)

盟國 (並查集的刪除操作)

題目連結

思路

並查集的刪除節點,基本的並查集只涉及合併和查詢,沒有刪除。
並查集的結構是樹形的,在刪除一個節點的同時還要保持其子節點與根節點的相對關係,是很麻煩的,所以我們的做法就是不去刪除,而是重新為它開闢空間來進行儲存。
也就是再開一個info陣列,來儲存各個節點的關係的儲存位置,在用一個par陣列來維護節點之間的關係,由於大量的刪除,所以info陣列要遠大於par陣列。
對於整個程式碼來說,並查集的操作並不需要變化,只需要新增一個del函式。例如查詢 i 的根節點時,需要呼叫info[i]。

程式碼

#include <cstring>
#include <cstdio>
#include <set> using namespace std; const int maxn = 100000; int info[maxn+10]; int par[maxn*10]; bool vis[maxn*10]; int nCount; void init(int n) { for(int i=0; i<n; i++) { par[i] = info[i] = i; } nCount = n; } int get_par(int a) { if(par[a]==a) return a; else return
par[a] = get_par(par[a]); } void merge(int a, int b) { int pa = get_par(a); int pb = get_par(b); if(pa!=pb) par[pb] = pa; } // 刪除節點 void del(int a) { // 額外分配空間 info[a] = nCount; par[nCount] = nCount; nCount++; } int main() { int n, m, tt=1; int a, b; char
op[5]; while(scanf("%d%d", &n, &m)&&n) { init(n); for(int i=0; i<m; i++) { scanf("%s%d", op, &a); if(op[0]=='M') { scanf("%d", &b); merge(info[a], info[b]); } else { del(a); } } memset(vis, false, sizeof(vis)); int re = 0; for(int i=0; i<n; i++) { int temp = get_par(info[i]); if(!vis[temp]) { vis[temp] = true; re++; } } printf("Case #%d: %d\n", tt++, re); } return 0; }