给定两种操作
第一种是合并X Y
第二种是把X分离出来,就是从原来的集合中分离出来,其它的关系不变。
关键是怎么分离,可以考虑把它变成一个其它值。HASH[i] = other_val
然后用新值去做并查集即可、
需要注意的一点就是
加入现在根是1,fa[1] = 1, fa[2] = 1, fa[3] = 1
那么如果你删除了1,这应该输出2.但是现在是fa[2] = 1,是一个不存在的根了,这个时候ans应该+1,但是按照并查集的思路
if (find(HASH[i]) == HASH[i]) ans++ 是不行的,因为这个根已经不存在了。
解决方法就是标记是否为虚根,del[i] = true表示删除了,但是枚举fa[3]的时候就要避免重新加,需要取消标记。
如果这时再有M 4 2,那么就把fa[4] = 1,用虚根表示即可。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 2e6 + 20; int fa[maxn]; int HASH[maxn]; bool del[maxn]; void init() { memset(del, 0, sizeof del); for (int i = 0; i < maxn; ++i) fa[i] = i; for (int i = 0; i < maxn; ++i) HASH[i] = i; } int find(int u) { if (fa[u] == u) return u; else return fa[u] = find(fa[u]); } void merge(int x, int y) { x = find(x); y = find(y); if (x != y) { fa[y] = x; } } int n, m; int ff; void work() { init(); int t = n; for (int i = 1; i <= m; ++i) { char str[11]; int a, b; scanf("%s", str); if (str[0] == ‘M‘) { scanf("%d%d", &a, &b); merge(HASH[a], HASH[b]); } else { scanf("%d", &a); del[HASH[a]] = 1; HASH[a] = n++; } } int ans = 0; for (int i = 0; i < t; ++i) { if (find(HASH[i]) == HASH[i]) ans++; if (del[find(HASH[i])]) { del[find(HASH[i])] = 0; ans++; } } // cout << find(2) << endl; printf("Case #%d: %d\n", ++ff, ans); return; } int main() { #ifdef local freopen("data.txt","r",stdin); #endif while (scanf("%d%d", &n, &m) != EOF && (n + m)) { work(); } return 0; }
HDU 2473 Junk-Mail Filter 并查集,虚拟删除操作
原文:http://www.cnblogs.com/liuweimingcprogram/p/5928320.html