带权并查集与普通并查集的区别在于,其每条边/每个点都有权值。因此,我们需要额外用一个数组来维护每个点 x x x 到其当前祖先 f a x fa_x fax 的权值和,并在路径压缩的时候维护。
直接说可能较难理解,于是下面是一道例题。
传送门
考虑对于每个点 x x x,维护其到其祖先 f a x fa_x fax 的距离 d e p x dep_x depx,并对于每个并查集的根维护连通块大小 s i z siz siz。
对于一个 M
操作,令涉及到的战舰分别为 x , y x,y x,y,我们找到 x x x 的祖先 f x fx fx 以及 y y y 的祖先 f y fy fy。然后,我们更新 f a f x : = f y fa_{fx}:=fy fafx:=fy 以及 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} depfx:=sizfy,sizfy:=sizfy+sizfx。
对于一个 C
操作,我们先判断 f x fx fx 是否与 f y fy fy 相等。若不相等,则输出 − 1 -1 −1;否则答案是 ∣ d e p f x − d e p f y ∣ − 1 |dep_{fx}-dep_{fy}|-1 ∣depfx−depfy∣−1。
这样本题是否就被解决了呢?如果你足够细心,你会发现: 在一次 M
操作过后, f x fx fx 的 d e p dep dep 以及 f a fa fa 产生了变化,而这会导致整棵以 f x fx fx 为根的子树的 d e p , f a dep,fa dep,fa 产生变化,然而我们并没有更新 ⋯ ⋯ \cdots \cdots ⋯⋯
其实并没有关系。当我们查询某个节点 u u u 的信息的时候,我们并不直接查询 d e p u dep_{u} depu,而是先对 u u u 进行路径压缩,并在路径压缩的过程中更新信息,最后再查询 d e p u dep_u depu。具体来说,在路径压缩的过程中,我们先求出 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} depu:=depu+depfau(注意这里是 f a u fa_u fau 而不是 A A A),并在回溯的时候令 f a u = A fa_u=A fau=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} depu:=depu+depfau 的实际意义如下:
原来 d e p u dep_u depu 维护的是从 u u u 到 f a u fa_u fau 的距离,而现在需要将其改为从 u u u 到 A A A 的距离。显然,从 u u u 到 A A A 的距离,就是从 u u u 到 f a u fa_u fau 的距离加上从 f a u fa_u fau 到 A A A 的距离。因此, d e p u dep_u depu 的新值应该是 d e p u + d e p f a u dep_u+dep_{fa_u} depu+depfau。注意,此时 d e p f a u dep_{fa_u} depfau 已经被更新过。
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