如果不采用路径压缩而只采用按秩合并,那么并查集的可持久化是比较容易实现的。按秩合并可以保证一棵 $n$ 个节点的树的高度是 $O(log n)$ 的。
实现方法:
用 $r_v$ 表示 $v$ 所在子树的根。
假设要将点 $u$ 和点 $v$ 所在子树和并(也就是将边 $(u,v)$ 加入图中),那么需要在合并之前记录一下 $r_u, mathrm{rank}(r_u)$ 和 $r_v, mathrm{rank}(r_v)$。
要恢复到某个历史版本,就按与加边相反的顺序删边。
const int N = 5e5 + 5;
int par[N];
int rk[N];
int root(int x){
while(x != par[x]) x = par[x];
return x;
}
struct his{
int u, rk1;
int v, rk2;
};
his unite(int x, int y){
x = root(x);
y = root(y);
his res = {x, rk[x], y, rk[y]};
if(x == y) return res;
if(rk[x] > rk[y])
par[y] = x;
else{
par[x] = y;
if(rk[x] == rk[y])
++rk[y];
}
return res;
}
void divide(const his &x){
par[x.u] = x.u;
par[x.v] = x.v;
rk[x.u] = x.rk1;
rk[x.v] = x.rk2;
}