AC通道 :https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3138
[分析]
操作1与操作3显然是简单的加权并查集,而操作2只需要将并查集添上一个移动元素的功能就可以了。
当我们将元素A移动到集合B中时,显然不能直接移动A,否则会将元素A在其原集合中的儿子元素也跟着移动。
这时,我们可以新增一个元素用来代替A,原集合中的A可以只起一个转移的作用,将那个新增的元素与集合B合并即可。
注意:因为这是加权并查集,所以在移动的时候要注意适当增减原集合与新集合的权值。
具体实现方法见代码:
#include <iostream> #include <cstdio> using namespace std; int n,m,cnt; int t[300010];//集合中的元素个数 int s[300010];//集合中的权值之和 int num[300010];//新增的元素的权值 int fa[300010];//father int nxt[300010];//保存原元素对应的新增的元素 int find(int x){ int tmp=x,pre; while(tmp!=fa[tmp])tmp=fa[tmp]; while(x!=tmp){ pre=fa[x]; fa[x]=tmp; x=pre; } return tmp; } void merge(int x,int y){ int fx=find(x),fy=find(y); if(fx!=fy){ fa[fx]=fy; t[fy]+=t[fx]; s[fy]+=s[fx]; } } void move(int x,int y,int real){ int fx=find(x),fy=find(y); if(fx==fy)return; cnt++; fa[cnt]=cnt; s[fx]-=num[x]; t[fx]--; s[cnt]=num[x]; num[cnt]=num[x]; t[cnt]=1; nxt[real]=cnt; merge(cnt,fy); } void print(int x){ int fx=find(x); printf("%d %d\n",t[fx],s[fx]); } void PENGYIHAO_WORK(){ for(int i=1;i<=m;i++){ int a,b,c; scanf("%d%d",&a,&b); if(a==1){scanf("%d",&c);merge(nxt[b],nxt[c]);} else if(a==2){scanf("%d",&c);move(nxt[b],nxt[c],b);} else if(a==3){print(nxt[b]);} } } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ for(int i=1;i<=n;i++)fa[i]=i,s[i]=i,t[i]=1,num[i]=i,nxt[i]=i; cnt=n; PENGYIHAO_WORK(); } return 0; }