UVA11987 - Almost Union-Find (并查集带删除)
题目大意:给出三个操作: 1 p q 表示将p q 这两个数所在的集合合并在一起。2 p q表示将p这个数从原有的集合中拿出来放到q所在的集合中。3 p表示查询p所在的集合总共有几个元素,和是多少。
解题思路:并查集。只是并查集中并没有删除的操作。所以就需要将删除的这个点的影响降到0,也就是给删除的点申请一个新的id,以后都是用这个新的id来表示这个点,这样原来的那个集合里的点p就没意义,自然影响就为0.
代码:
#include <cstdio>
#include <cstring>
const int maxn = 2e5 + 5;
int n, m;
int parent[maxn], cnt[maxn], sum[maxn];
int id[maxn];
int dex;
void init () {
for (int i = 0; i <= n; i++) {
cnt[i] = 1;
sum[i] = id[i] = parent[i] = i;
}
dex = n;
}
int getParent (int a) {
return a == parent[a] ? a: parent[a] = getParent (parent[a]);
}
void Union (int a, int b) {
int p = getParent (a);
int q = getParent (b);
if (p != q) {
parent[p] = q;
cnt[q] += cnt[p];
sum[q] += sum[p];
}
}
void move (int a) {
int p = getParent (id[a]);
cnt[p]--;
sum[p] -= a;
id[a] = ++dex;
parent[dex] = dex;
cnt[dex] = 1;
sum[dex] = a;
}
int main () {
int type;
int a, b;
while (scanf ("%d%d", &n, &m) != EOF) {
init();
while (m--) {
scanf ("%d", &type);
if (type == 1) {
scanf ("%d%d", &a, &b);
Union (id[a], id[b]);
} else if (type == 2) {
scanf ("%d%d", &a, &b);
int p = getParent (id[a]);
int q = getParent (id[b]);
if (p != q) {
move(a);
Union(id[a], id[b]);
}
} else {
scanf ("%d", &a);
int p = getParent (id[a]);
printf ("%d %d\n", cnt[p], sum[p]);
}
}
}
return 0;
}