【题目大意】
初始给定n个集合:{1},{2},…,{n},要求支持三种操作:
"1 p q":若p,q不在同一集合,将它们所在的集合合并成一个
"2 p q":若p,q不在同一集合,将元素p移动到q所在的集合
"3 p" :询问p所在集合的元素个数及元素和
总共m个操作,1<=n,m<=10^5
【题解】
维护sum[x],cnt[x],表示以x为father的集合的 元素和及元素个数,就可以解决操作1和3
对于操作2,若直接把p的爸爸变为father(q),就会导致p的孩子所在的集合也变为father(q)
所以这里不能直接对p做修改。
要先移出p这个元素,可以考虑 先“使p对于原集合无效”,即删除father(p)上的对应信息, 然后新建一个元素使它与p等效,并记录这个与p等效的新元素的编号id[p],以后再使用p时用id[p]代替就行了
使这个id[p]成为一个单独集合并完善相关信息,p这个元素就算成功被移出了
再把这个单独集合与q所在的集合合并就行了
【代码】
#include<stdio.h>
#include<stdlib.h>
typedef long long LL;
LL sum[200005]={0};
int fa[200005]={0},id[200005]={0},cnt[200005]={0};
int n;
int father(int x)
{
if(x!=fa[x]) fa[x]=father(fa[x]);
return fa[x];
}
void hb(int x,int y)
{
int fx=father(id[x]),fy=father(id[y]);
fa[fx]=fy;
sum[fy]+=sum[fx];
cnt[fy]+=cnt[fx];
}
void get(int x)
{
int fx=father(id[x]);
cnt[fx]--;
sum[fx]-=(LL)x;
id[x]=++n;
fa[id[x]]=id[x];
cnt[id[x]]=1;//不能写成"cnt[id[x]]++",因为未对新建元素初始化
sum[id[x]]=(LL)x;
}
int main()
{
int m,i,op,p,q;
while(scanf("%d%d",&n,&m)==2)
{
for(i=1;i<=n;i++)
{
fa[i]=id[i]=i;
sum[i]=(LL)i;
cnt[i]=1;
}
for(;m>0;m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&p,&q);
if(father(id[p])!=father(id[q])) hb(p,q);
}
if(op==2)
{
scanf("%d%d",&p,&q);
if(father(id[p])!=father(id[q]))
{
get(p);
hb(p,q);
}
}
if(op==3)
{
scanf("%d",&p);
printf("%d %lld\n",cnt[father(id[p])],sum[father(id[p])]);
}
}
}
return 0;
}