带权并查集 详解

  • Post author:
  • Post category:其他




简介

带权并查集与普通并查集的区别在于,其每条边/每个点都有权值。因此,我们需要额外用一个数组来维护每个点



x

x






x





到其

当前

祖先



f

a

x

fa_x






f



a










x





















的权值和,并

在路径压缩的时候

维护。

直接说可能较难理解,于是下面是一道例题。



P1169



Description


传送门



Solution

考虑对于每个点



x

x






x





,维护其到其祖先



f

a

x

fa_x






f



a










x





















的距离



d

e

p

x

dep_x






d


e



p










x





















,并对于每个并查集的根维护连通块大小



s

i

z

siz






s


i


z





对于一个

M

操作,令涉及到的战舰分别为



x

,

y

x,y






x


,




y





,我们找到



x

x






x





的祖先



f

x

fx






f


x





以及



y

y






y





的祖先



f

y

fy






f


y





。然后,我们更新



f

a

f

x

:

=

f

y

fa_{fx}:=fy






f



a











f


x





















:






=








f


y





以及



d

e

p

f

x

:

=

s

i

z

f

y

,

s

i

z

f

y

:

=

s

i

z

f

y

+

s

i

z

f

x

dep_{fx}:=siz_{fy}, siz_{fy}:=siz_{fy}+siz_{fx}






d


e



p











f


x





















:






=








s


i



z











f


y



















,




s


i



z











f


y





















:






=








s


i



z











f


y





















+








s


i



z











f


x






















对于一个

C

操作,我们先判断



f

x

fx






f


x





是否与



f

y

fy






f


y





相等。若不相等,则输出



1

-1









1





;否则答案是



d

e

p

f

x

d

e

p

f

y

1

|dep_{fx}-dep_{fy}|-1









d


e



p











f


x






























d


e



p











f


y

































1





这样本题是否就被解决了呢?如果你足够细心,你会发现: 在一次

M

操作过后,



f

x

fx






f


x









d

e

p

dep






d


e


p





以及



f

a

fa






f


a





产生了变化,而这会导致整棵以



f

x

fx






f


x





为根的子树的



d

e

p

,

f

a

dep,fa






d


e


p


,




f


a





产生变化,然而我们并没有更新



\cdots \cdots
















其实并没有关系。当我们查询某个节点



u

u






u





的信息的时候,我们并不直接查询



d

e

p

u

dep_{u}






d


e



p











u






















,而是先对



u

u






u





进行路径压缩,并在路径压缩的过程中更新信息,最后再查询



d

e

p

u

dep_u






d


e



p










u





















。具体来说,在路径压缩的过程中,我们先求出



u

u






u





的祖先



A

A






A





,然后执行



d

e

p

u

:

=

d

e

p

u

+

d

e

p

f

a

u

dep_u:=dep_u+dep_{fa_u}






d


e



p










u




















:






=








d


e



p










u




















+








d


e



p











f



a










u






































(注意这里是



f

a

u

fa_u






f



a










u





















而不是



A

A






A





),并在回溯的时候令



f

a

u

=

A

fa_u=A






f



a










u




















=








A





int Find(int u){
	if (u==fa[u])  return u;
	
	int A=Find(fa[u]);
	dep[u]+=dep[fa[u]];
	return (fa[u]=A);
}

其中



d

e

p

u

:

=

d

e

p

u

+

d

e

p

f

a

u

dep_u:=dep_u+dep_{fa_u}






d


e



p










u




















:






=








d


e



p










u




















+








d


e



p











f



a










u






































的实际意义如下:

原来



d

e

p

u

dep_u






d


e



p










u





















维护的是从



u

u






u









f

a

u

fa_u






f



a










u





















的距离,而现在需要将其改为从



u

u






u









A

A






A





的距离。显然,从



u

u






u









A

A






A





的距离,就是从



u

u






u









f

a

u

fa_u






f



a










u





















的距离加上从



f

a

u

fa_u






f



a










u

























A

A






A





的距离。因此,



d

e

p

u

dep_u






d


e



p










u





















的新值应该是



d

e

p

u

+

d

e

p

f

a

u

dep_u+dep_{fa_u}






d


e



p










u




















+








d


e



p











f



a










u






































。注意,此时



d

e

p

f

a

u

dep_{fa_u}






d


e



p











f



a










u






































已经被更新过。

在这里插入图片描述



Code

const int maxl=30005;
int t,n,x,y;char opt;
int fa[maxl],dep[maxl],siz[maxl];

int Find(int x){
	if (x==fa[x])  return x;
	
	int new_fa=Find(fa[x]);
	dep[x]+=dep[fa[x]];
	return fa[x]=new_fa;
}

signed main(){
	t=read(),n=30000;
	for (int i=1;i<=n;i++)  fa[i]=i,siz[i]=1;
	
	while (t--){
		cin>>opt;x=read(),y=read();
		if (opt=='M'){
			int fx=Find(x),fy=Find(y);
			fa[fx]=fy;
			dep[fx]+=siz[fy];
			siz[fy]+=siz[fx];
		}
		else{
			int fx=Find(x),fy=Find(y);
			if (fx!=fy)  puts("-1");
			else printf("%d\n",abs(dep[x]-dep[y])-1);
		}
	}
	return 0;
}



带权并查集的其他应用

其实,带权并查集就是一个非常套路的东西。只要摸清楚了其本质就不难了。

下面是找来的几个题,可以写一写提升自己对带权并查集的熟练度。


LNR #1 D1T2



P3733



版权声明:本文为Cherrt原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。