题目链接:模板题
普通的并查集是通过 数组来实现的(或者说是 数组)
可持久化并查集,就是用主席树来维护并查集的 数组(因为主席树可以记录过去的版本),使之能回退到过去的版本,这里的版本指的是某个操作之前的 数组
可持久化并查集的合并部分使用的不是路径压缩来优化,而是使用启发式合并:两棵树合并时,将树深小的合并到树深大的,这样树的高度增长比较慢。
若合并两棵树,新的树的高度会增长,那么树的 至少扩大两倍,因此树高至多增长 次,每次增长的值为 1。
主席树部分:
先建一棵初始的主席树,令每个下标 的
void build(int &rt,int l,int r) {
rt = ++sz;
if(l == r) {
dep[rt] = 1;
fa[rt] = l;
return ;
}
int mid = l + r >> 1;
build(lson[rt],l,mid);
build(rson[rt],mid + 1,r);
}
更新:更新是单点更新,套用主席树的模板即可,因为 dep 和 fa 只关心叶子结点的值,其他节点的值不用继承
void upd(int p,int v,int &rt,int l,int r) {
sz++;
lson[sz] = lson[rt];
rson[sz] = rson[rt];
rt = sz;
if(l == r) {
dep[sz] = dep[rt];
fa[rt] = v;
return ;
}
int mid = l + r >> 1;
if(p <= mid) upd(p,v,lson[rt],l,mid);
else upd(p,v,rson[rt],mid + 1,r);
}
查询部分,查询某个结点的父节点,注意不是直接返回并查集的父节点,而是返回下标,因为后面还要比较深度,返回下标更方便
int qry(int p,int rt,int l,int r) {
if(l == r) return rt;
int mid = l + r >> 1;
if(p <= mid) return qry(p,lson[rt],l,mid);
else return qry(p,rson[rt],mid + 1,r);
}
并查集部分:
查找根节点:因为启发式合并下树高最多log,直接暴力查找,复杂度为两个log
int find(int x,int rt) {
int fx = qry(x,rt,1,n);
if(fa[fx] == x) return fx;
return find(fa[fx],rt);
}
修改深度:当合并时新的树的深度需要修改时,最多 + 1,因此单点修改 + 1即可
void add(int p,int rt,int l,int r) {
if(l == r) {
dep[rt]++;
return ;
}
int mid = l + r >> 1;
if(p <= mid) add(p,lson[rt],l,mid);
else add(p,rson[rt],mid + 1,r);
}
模板题代码:
#include<iostream>
#include<stdio.h>
using namespace std;
const int maxn = 2e5 + 10;
int n,m;
//用主席树来维护并查集的fa数组,还需要维护一个dep数组以便启发式合并
int root[maxn],lson[maxn * 30],rson[maxn * 30],dep[maxn * 30],fa[maxn * 30],sz;
int read() {
int s=0,m=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
}
void build(int &rt,int l,int r) {
rt = ++sz;
if(l == r) {
dep[rt] = 1;
fa[rt] = l;
return ;
}
int mid = l + r >> 1;
build(lson[rt],l,mid);
build(rson[rt],mid + 1,r);
}
void upd(int p,int v,int &rt,int l,int r) {
sz++;
lson[sz] = lson[rt];
rson[sz] = rson[rt];
rt = sz;
if(l == r) {
dep[sz] = dep[rt];
fa[rt] = v;
return ;
}
int mid = l + r >> 1;
if(p <= mid) upd(p,v,lson[rt],l,mid);
else upd(p,v,rson[rt],mid + 1,r);
}
int qry(int p,int rt,int l,int r) {
if(l == r) return rt;
int mid = l + r >> 1;
if(p <= mid) return qry(p,lson[rt],l,mid);
else return qry(p,rson[rt],mid + 1,r);
}
int find(int x,int rt) {
int fx = qry(x,rt,1,n);
if(fa[fx] == x) return fx;
return find(fa[fx],rt);
}
void add(int p,int rt,int l,int r) {
if(l == r) {
dep[rt]++;
return ;
}
int mid = l + r >> 1;
if(p <= mid) add(p,lson[rt],l,mid);
else add(p,rson[rt],mid + 1,r);
}
int main() {
n = read(); m = read();
build(root[0],1,n);
for(int i = 1; i <= m; i++) {
int op,x,y;
root[i] = root[i - 1];
op = read();
if(op == 1) {
x = read(); y = read();
int fx = find(x,root[i]);
int fy = find(y,root[i]);
if(fa[fx] == fa[fy]) continue;
if(dep[fx] > dep[fy]) swap(fx,fy);
upd(fa[fx],fa[fy],root[i],1,n);
if(dep[fx] + 1 > dep[fy]) add(fa[fy],root[i],1,n);
} else if(op == 2) {
x = read();
root[i] = root[x];
} else if(op == 3) {
x = read(); y = read();
int fx = find(x,root[i]);
int fy = find(y,root[i]);
if(fa[fx] != fa[fy]) puts("0");
else puts("1");
}
}
return 0;
}
来源:CSDN
作者:猝死在学ACM的路上
链接:https://blog.csdn.net/qq_41997978/article/details/103808745