盟國 (並查集的刪除操作)
阿新 • • 發佈:2019-02-04
思路
並查集的刪除節點,基本的並查集只涉及合併和查詢,沒有刪除。
並查集的結構是樹形的,在刪除一個節點的同時還要保持其子節點與根節點的相對關係,是很麻煩的,所以我們的做法就是不去刪除,而是重新為它開闢空間來進行儲存。
也就是再開一個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;
}