Almost Union-Find (并查集+删除元素)题解

时间:2024-11-25 00:07:25

Almost Union-Find (并查集+删除元素)题解

思路:

当时只有暴力的思想,1、3都很好达到,只要加一个sum[ ]和num[ ]就可以,但是2比较麻烦,不知道谁以p为根,就想直接扫描然后一个个和p脱离关系,果然超时emmm。通常的想法是我们先用一个数组id[ ]表示p所代表的代号也就是说p当前的根为:

pre[ id[p] ]

所以当p被2操作时,我们只要改变它的id[p](比如 id[p]=++n),我们就可以让p有一个新的pre[ ]表示,而不用改变原来的根的表示,所以当后面的操作又用到p时,现在的 pre[ id[p] ] 已经不是之前那个了。

详细可以看代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<map>
#define N 200010
#define HASH 10000
using namespace std;
int pre[N],num[N],id[N];
long long sum[N];
int find(int a){
int r=a;
while(r!=pre[r]){
r=pre[r];
}
int i=a,j;
while(i!=pre[i]){
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void init(int n){
int i;
for(i=0;i<=n;i++){
pre[i]=i;
sum[i]=i;
id[i]=i;
num[i]=1;
}
}
int join(int p,int q){ //合并两个根(注意:方向必须是这样)
int fp=find(id[p]);
int fq=find(id[q]);
pre[fp]=fq;
num[fq]+=num[fp];
sum[fq]+=sum[fp];
}
int main(){
int n,m,c,p,q,i;
while(scanf("%d%d",&n,&m)!=EOF){
init(n);
while(m--){
scanf("%d",&c);
if(c==1){
scanf("%d%d",&p,&q);
if(find(id[p])!=find(id[q]))
join(p,q);
}
else if(c==2){
scanf("%d%d",&p,&q);
if(find(id[p])!=find(id[q])){
num[find(id[p])]--; //脱离与原根关系
sum[find(id[p])]-=p;
id[p]=++n; //重新申请一个id
pre[id[p]]=id[p]; //初始化
sum[id[p]]=p;
num[id[p]]=1;
join(p,q); //建立新关系
}
}
else{
scanf("%d",&p);
printf("%d %d\n",num[find(id[p])],sum[find(id[p])]);
}
}
}
return 0;
}