HDU 2473 Junk-Mail Filter(并查集 | 删除操作)

该题比较经典,是并查集的删除操作。 并查集一般是用来合并和判断是否在一个集合的,因为这种树状结构如果真的删除一个结点,难说其结构会如何变化,难以高效的维护。

但是我们仍然有办法解决,我们可以通过给每个结点不断更新编号,让删除的点变成虚点,由于最多进行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;
}

你可能感兴趣的:(数据结构,并查集,ACM-ICPC)