题意:有n个数,刚开始每个数都是一个单独的集合里,给定下列三种操作
1 p q :将p 和 q 合并在一个集合里
2 p q :将p 放在 q所在的集合里
3 p : 输出p 所在的集合的元素个数和元素的和。
思路:对于1操作 如果p q 祖先不同,那么merge(p,q)即可;对于3 操作,只需找到p的祖先,然后输出这个集合的元素个数和元素的和,对于 2 操作,我们不能直接合并 p q ,因为这样 p的子孙们也归到的q中 这是不符合题意的,在这里,我们 需要新开一个结点,让它单独成为一个集合,并且让 p 指向这个结点,这样 我们操作的时候 是对 这个结点进行操作,而不改变 原先p 的位置,我们定义一个 数组idx【i】 它表示 i指向的结点。这样的话 将idx【i】与 q合并, 同时 在p的集合里删去p这个点即可。详情见代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#define inf 0x3f3f3f3f
#define LL long long
#include<algorithm>
using namespace std;
int f[100010],idx[100010],num[100010],sum[300010];
int n,m;
void init()
{
for(int i=1; i<=100010; i++)
{
f[i]=i;
idx[i]=i;
num[i]=1;
sum[i]=i;
}
return ;
}
int getf(int v)
{
if(f[v]==v)
return v;
int s=getf(f[v]);
sum[v]=sum[v]+sum[f[v]];
num[v]=num[v]+num[f[v]];
return f[v]=s;
}
void merge(int u,int v)
{
int t1=getf(idx[u]);
int t2=getf(idx[v]);
if(t1!=t2)
{
f[t2]=t1;
sum[t1]=sum[t1]+sum[t2];
num[t1]=num[t1]+num[t2];
}
return ;
}
void build(int p)
{
int k=++n;
idx[p]=k;
f[k]=k;
sum[k]=p;
num[k]=1;
}
int main()
{
int x,p,q;
while(~scanf("%d %d",&n,&m))
{
init();
for(int i=0; i<m; i++)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d %d",&p,&q);
int t1=getf(idx[p]);
int t2=getf(idx[q]);
if(t1!=t2)
merge(p,q);
}
else if(x==2)
{
scanf("%d %d",&p,&q);
int t1=getf(idx[p]);
int t2=getf(idx[q]);
if(t1!=t2)
{
int f=getf(idx[p]);
sum[f]=sum[f]-p;
num[f]--;
build(p);
merge(p,q);
}
}
else
{
scanf("%d",&p);
int t=getf(idx[p]);
printf("%d %d\n",num[t],sum[t]);
}
}
}
return 0;
}