题目大意:
题目中给出几个操作,1:把p、q两个集合合并;2:把p移到集合q里面;3:输出p所在集合的元素数量以及这些元素的和。
题目思路:
两个集合合并我们知道,就是把p的根节点接在q的根节点上,而把p移到集合q里呢?仅仅令 f a [ p ] = q 的根节点就好了吗?
当然不行。这样做我们只是把p移到了集合q里,但是如果p不是叶子节点,那么p下面的儿子们也随之移到了集合q里。
但是我们可以这样想,能不能把p点单独分离出来,让它单独成为一个集合,那么就相当于把{p}与集合q合并,不用担心下面孩子也会动的问题了;
具体实现:开一个id[]数组用来保存p的信息即可。
至于输出集合的元素及他们的和我们可以分别用num[]和sum[]来随时保存。(移动的时候做相应加减)
代码:
#include<iostream>
#include<cstdio>
#define maxn 300005
using namespace std;
int fa[maxn];
int num[maxn],sum[maxn];
int id[maxn];//怎么不把儿子们带过去呢……
int n,m;
int finds(int x)
{
return fa[x]==x?x:finds(fa[x]);
}
void remove1(int a,int b)//连根一起移过去
{
a=finds(a);
b=finds(b);
if(a!=b)
{
fa[a]=b;
num[b]+=num[a];
num[a]=0;//以aa为根节点的集合中元素数为0
sum[b]+=sum[a];
sum[a]=0;
}
}
void remove2(int a,int b)//只移动a
{
int aa=finds(id[a]);
int bb=finds(b);
if(aa!=bb)
{
id[a]=++n;
fa[n]=bb;
num[bb]++;
num[aa]--;
sum[bb]+=a;
sum[aa]-=a;
}
}
int main(void)
{
int d,p,q;
int pp,qq;
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
{
id[i]=i;
num[i]=1;
sum[i]=i;
fa[i]=i;
}
for(int i=0;i<m;i++)
{
scanf("%d",&d);
switch(d)
{
case 1:
scanf("%d%d",&p,&q);
remove1(id[p],id[q]);
break;
case 2:
scanf("%d%d",&p,&q);
remove2(p,id[q]);//后面还需要p
break;
case 3:
scanf("%d",&p);
pp=finds(id[p]);
printf("%d %d\n",num[pp],sum[pp]);//以根节点为代表
break;
}
}
}
return 0;
}
呼呼