您的位置:首页 > 其它

UVA 11987 Almost Union-Find(带删除的并查集)

2016-10-06 17:47 323 查看
题意:

初始时,一共有n个元素的组合1,2,3....n

给出三个操作

1 p q:合并p,q所在的集合

2 p q:把p移动到q所在的集合

3 p:输出p所在的集合的元素的个数

思路:1,3相当简单,赤裸裸的并查集就好了 
麻烦的是2,并查集是单向的,只知道父亲不知道儿子 
所有如果i是叶子节点无所谓 
如果是父亲节点就会十分的蛋疼: 
你是到另一个集合里面去了,你的儿子们呢? 
把i这个点复制一下(编号为cnt++),当作叶子节点扔到新集合里去 
原来的i仅仅有作为集合编号的作用,可以认为是个空点 ,只是为了不让原来的并查集出现错误,防止他的儿子们找不到根节点

重新找一个节点,记录这个节点自己的信息,也就是sum = 他自己, num = 1;

注意:合并或者查询等的时候一定要合并id[i],因为原来的i只是一个空节点了,为了保证并查集不出错而存在的,在del函数的时候,传参

要穿本身,因为一会要用到本身,新的节点的sum是他本身,并且对id数组更新的时候,下标也是他本身

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 2e5 + 5;
int pre[maxn], n, m, num[maxn], sum[maxn], id[maxn], cnt;
int Find(int x) {return x == pre[x] ? x : pre[x] = Find(pre[x]); }
void join(int x, int y)
{
if(Find(x) != Find(y))
{
num[Find(x)] += num[Find(y)]; //找并查集有几个元素或者和的时候直接重新开一个数组记录就行
sum[Find(x)] += sum[Find(y)];
pre[Find(y)] = Find(x);
}
}
void del(int x)
{
num[Find(id[x])]--; //注意 这里一定要对id[x]做操作。。。
sum[Find(id[x])] -= x;
cnt++;
pre[cnt] = cnt;
num[cnt] = 1; //这里之后就不能用id[x]了,把这个元素合到另一个集合,他的和应该是他自己;
sum[cnt] = x;
id[x] = cnt; //如果传参把id[x]传进来,这里就不对了,因为id[x]记录的是新的地址。。也就是cnt++..这里是要给x更新地址
}
int main()
{
int a, b, c;
while(~scanf("%d%d",&n,&m))
{
for(int i = 1; i <= n; i++) pre[i] = i, id[i] = i,sum[i] = i, num[i] = 1;
cnt = n;
while(m--)
{
scanf("%d",&a);
if(a == 1)
{
scanf("%d%d",&b,&c);
join(id[b],id[c]);
}
if(a == 2)
{
scanf("%d%d",&b,&c);
if(Find(id[b]) != Find(id[c]))
{
del(b);
join(id[b],id[c]);
}

// pre[b] = Find(c);
}
if(a == 3)
{
scanf("%d",&b);
int r = Find(id[b]);
printf("%d %d\n",num[r],sum[r]);
}
}
}
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: