简介
带权并查集与普通并查集的区别在于,其每条边/每个点都有权值。因此,我们需要额外用一个数组来维护每个点
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;
}
带权并查集的其他应用
其实,带权并查集就是一个非常套路的东西。只要摸清楚了其本质就不难了。
下面是找来的几个题,可以写一写提升自己对带权并查集的熟练度。