本题难点就是第二个操作。如果可以保证移动的节点一定是叶子节点,那就可以直接修改fa数组了。
为了实现这点,可以开2×n个节点,初始时i的父节点是i+n,这样在合并时可以保证前n个节点一定一直都是叶子节点,问题就变成简单并查集了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200005
int fa[maxn];
int num[maxn];
int sum[maxn];
int N,M;
void init(){
for(int i=1;i<=N;i++){
fa[i]=i+N;
sum[i]=i;
num[i]=1;
}
for(int i=N+1;i<=2*N;i++){
fa[i]=i;
sum[i]=i-N;
num[i]=1;
}
}
int Find(int n){
if(n!=fa[n]) return fa[n]=Find(fa[n]);
return fa[n];
}
void Union(int x,int y){
if(Find(x)==Find(y)) return;
int tx=Find(x),ty=Find(y);
fa[tx]=ty;
num[ty]+=num[tx];
sum[ty]+=sum[tx];
}
void Move(int x,int y){
int tx=Find(x),ty=Find(y);
num[tx]--;
sum[tx]-=x;
num[ty]++;
sum[ty]+=x;
fa[x]=ty;
}
void Show(int x){
int tx=Find(x);
printf("%d %d\n",num[tx],sum[tx]);
}
int main(){
while(~scanf("%d%d",&N,&M)){
init();
for(int i=0;i<M;i++){
int k;
scanf("%d",&k);
int x,y;
if(k==1){
scanf("%d%d",&x,&y);
Union(x,y);
}
else if(k==2){
scanf("%d%d",&x,&y);
Move(x,y);
}
else {
scanf("%d",&x);
Show(x);
}
}
}
return 0;
}
/*
3 6
1 1 2
2 2 3
3 2
3 3
3 1
*/