UVA 11987 - Almost Union-Find(并查集“删除”)
题目链接 https://cn.vjudge.net/problem/UVA-11987
【题意】
题意:初始有N个集合,分别为 1 ,2 ,3 …..n。有三种操件
1 p q 合并元素p和q的集合
2 p q 把p元素移到q集合中
3 p 输出p元素集合的个数及所有元素和
【思路】
并查集维护两个值一个是集合中元素个数,一个是总和,那么最难处理的就是操作2,把p元素移动到q元素所在集合中,相当于把p元素从所在集合中删掉. 而并查集没有删除操作,所以这里用了一个等价的做法是把p对应的根结点的信息更新(个数减一,和减去p),然后在申请一个新节点id[p]来表示p元素
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005<<1;
int n,m;
int par[maxn],id[maxn],pos;
int cnt[maxn];
ll sum[maxn];
void init(){
for(int i=1;i<maxn;++i){
par[i]=i;
sum[i]=(ll)i;
cnt[i]=1;
id[i]=i;
}
pos=n+1;
}
int find(int x){
return par[x]==x?x:par[x]=find(par[x]);
}
int main(){
while(scanf("%d%d",&n,&m)==2){
init();
while(m--){
int tp;
scanf("%d",&tp);
if(tp==1){
int x,y;
scanf("%d%d",&x,&y);
int r1=find(id[x]);
int r2=find(id[y]);
if(r1!=r2){
par[r1]=r2;
sum[r2]+=sum[r1];
cnt[r2]+=cnt[r1];
}
}
else if(tp==2){
int x,y;
scanf("%d%d",&x,&y);
int r1=find(id[x]);
int r2=find(id[y]);
if(r1!=r2){
sum[r1]-=(ll)x;
--cnt[r1];
id[x]=pos++;
sum[id[x]]=(ll)x;
cnt[id[x]]=1;
par[id[x]]=r2;
sum[r2]+=sum[id[x]];
cnt[r2]+=cnt[id[x]];
}
}
else{
int x;
scanf("%d",&x);
int rt=find(id[x]);
printf("%d %lld\n",cnt[rt],sum[rt]);
}
}
}
return 0;
}