带权并查集 学习笔记

例题

[NOI2002] 银河英雄传说
题目传送门

算法实现

我们发现操作只有合并没有分解,所以考虑并查集。
但是好像普通的并查集貌似解决不了,所以我们要使用带权并查集。
我们需要维护两个数组:\(v,w\) , \(v_i\) 代表该节点以下的节点的个数, \(w_i\) 代表该节点以上的节点个数。
显然,节点 \(i,j\) 的距离(保证在同一队)是 \(|v_i-v_j|\)
考虑如何进行路径压缩。
看代码吧

int getfa(int x){
	if(x==f[x]) return x;
	int tmp=f[x];//以防f[x]改变
	f[x]=getfa(f[x]);
	w[x]+=w[tmp];//更新w[x]
	return f[x];
}

修改很简单,首先要把这条边接过去,然后处理上下的 \(w\)\(v\) 即可。

f[x]=y;
w[x]+=v[y];
v[y]+=v[x];

最后记得初始化

for(int i=1;i<=30000;i++) f[i]=i,v[i]=1,w[i]=0;

代码

已经进行防抄袭处理,删了几个地方

#include<cstdio>
#define abs(x) ((x)>0?(x):-(x))
#define maxn 30039
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
	Type sum=0;
	int flag=0;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-') c=getchar(),flag=1;
	while('0'<=c&&c<='9'){
		sum=(sum<<1)+(sum<<3)+(c^48);
		c=getchar();
	}
	if(flag) return -sum;
	return sum;
}
char c;
int T,a,b,x,y;
int f[maxn],v[maxn],w[maxn];
int getfa(int x){
	if(x==f[x]) return x;
	int tmp=f[x];
	f[x]=getfa(f[x]);
	w[x]+=w[tmp];
	return f[x];
}
int main(){
    for(int i=1;i<=30000;i++) f[i]=i,v[i]=1,w[i]=0;
    while(T--){
    	c=getchar(); while(c!='M'&&c!='C') c=getchar();
    	a=read(); b=read();
    	x=getfa(a); y=getfa(b);
    	if(c=='M'){
    		w[x]+=v[y];
    		v[y]+=v[x];
		}
    	else{
    		if(x!=y) printf("-1\n");
    		else printf("%d\n",abs(w[a]-w[b])-1);
		}
	}
	return 0;
}

posted @ 2021-03-25 20:42  jiangtaizhe001  阅读(31)  评论(0编辑  收藏  举报