思路
并查集的删除节点,基本的并查集只涉及合并和查询,没有删除。
并查集的结构是树形的,在删除一个节点的同时还要保持其子节点与根节点的相对关系,是很麻烦的,所以我们的做法就是不去删除,而是重新为它开辟空间来进行存储。
也就是再开一个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;
}