【题目大意】
初始给定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; }