该题比较经典,是并查集的删除操作。 并查集一般是用来合并和判断是否在一个集合的,因为这种树状结构如果真的删除一个结点,难说其结构会如何变化,难以高效的维护。
但是我们仍然有办法解决,我们可以通过给每个结点不断更新编号,让删除的点变成虚点,由于最多进行10^6次操作,因此,最多10^6+最大结点数个结点,开这么大的数组即可。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
const int INF = 1000000000;
const int maxn = 1000000 + 5 + 100000;
int n,q,x,y,kase = 0,cnt,m,a,b,p[maxn],id[maxn];
void init(int n,int m) {
for(int i=0;i<=n+m;i++) p[i] = i;//初始化并查集,注意要扩充所有可能结点
for(int i=0;i<n;i++) id[i] = i;//初始化结点编号
cnt = n;
}
int setfind(int x) {
return p[x] == x ? x : p[x] = setfind(p[x]);
}
char s[5];
set<int> S;
int main() {
while(~scanf("%d%d",&n,&m)) {
if(!n && !m) return 0;
init(n,m);
while(m--) {
scanf("%s",s);
if(s[0] == 'M') {
scanf("%d%d",&x,&y); //是对该结点的id进行操作
x = setfind(id[x]); y = setfind(id[y]);
if(x != y) p[x] = y;
}
else {
scanf("%d",&x);
id[x] = cnt++;//删除操作
}
}
int ans = 0; S.clear();
for(int i=0;i<n;++i)
S.insert(setfind(id[i]));
printf("Case #%d: %d\n",++kase,S.size());
}
return 0;
}