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
",num[find(id[p])],sum[find(id[p])]);
			}	
		}
	} 
	return 0;
}



原文地址:https://www.cnblogs.com/KirinSB/p/9409139.html