以洛谷
P
3402
\mathbf{P3402}
P3402为例
题目链接
\qquad
并查集+可持久化,想到用可持久化数组来维护并查集,因为并查集本质上也是用数组记录的
但是
不能使用路径压缩
\mathbf{不能使用路径压缩}
不能使用路径压缩,因为路径压缩会改变
f
a
fa
fa数组的值,持久化中,改变意味着需要
log
\log
log的空间来维护
\qquad
平时因为路径压缩后查询速度过快,导致按秩合并无人问津,但是在不能使用路径压缩时按秩合并的作用就体现出来了,按秩合并可以在
log
n
的时间
\mathbf{\log n的时间}
logn的时间内找到目标节点,在只有插入操作的情况下,按秩合并的总复杂度为
O
(
n
log
n
)
\mathbf{O(n\log n)}
O(nlogn),均摊
O
(
log
n
)
\mathbf{O(\log n)}
O(logn)
\qquad 用可持久化数组维护按秩合并的并查集时要维护两个数组,一个 r o o t f a [ v e r ] rootfa[ver] rootfa[ver]记录 v e r ver ver版本的并查集,一个 r o o t d e p [ v e r ] rootdep[ver] rootdep[ver]记录 v e r ver ver版本的深度(即秩)
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN = 2e5 + 5;
int rootfa[MAXN],rootdep[MAXN];
int top = 0;
int n;
struct Node{
int l,r,val;
}t[MAXN * 50];
inline int read(){
int n = 0,l = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') l = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
n = (n << 1) + (n << 3) + (c & 15);
c = getchar();
}
return n * l;
}
inline int built_tree(int l,int k,int r){
k = ++ top;
if(l == r){
t[k].val = l; //初始父亲即为自己
return k;
}
int mid = l + ((r - l) >> 1);
t[k].l = built_tree(l,t[k].l,mid);
t[k].r = built_tree(mid + 1,t[k].r,r);
return k;
}
inline int query(int l,int k,int r,int x){ //查询x节点的父亲或者深度
if(l == r) return t[k].val;
int mid = l + ((r - l) >> 1);
if(x <= mid) return query(l,t[k].l,mid,x);
else return query(mid + 1,t[k].r,r,x);
}
inline int find(int ver,int p){
int x = query(1,rootfa[ver],n,p);
if(p == x) return x;
else return find(ver,x);
}
inline int update(int l,int k,int r,int x,int y){ //并查集的合并,将x父亲改为y
t[++ top] = t[k];
k = top;
if(l == r){
t[k].val = y;
return k;
}
int mid = l + ((r - l) >> 1);
if(x <= mid) t[k].l = update(l,t[k].l,mid,x,y);
else t[k].r = update(mid + 1,t[k].r,r,x,y);
return k;
}
void mer(int x,int ver,int y){ //在版本ver下,合并x和y
int f1 = find(ver - 1,x);
int f2 = find(ver - 1,y);
if(f1 == f2){
rootfa[ver] = rootfa[ver - 1];
rootdep[ver] = rootdep[ver - 1];
return;
}
int d1 = query(1,rootdep[ver - 1],n,f1);
int d2 = query(1,rootdep[ver - 1],n,f2);
if(d1 < d2){
rootfa[ver] = update(1,rootfa[ver - 1],n,f1,f2);
rootdep[ver] = rootdep[ver - 1];
}
else if(d2 < d1){
rootfa[ver] = update(1,rootfa[ver - 1],n,f2,f1);
rootdep[ver] = rootdep[ver - 1];
}
else {
rootfa[ver] = update(1,rootfa[ver - 1],n,f1,f2);
rootdep[ver] = update(1,rootdep[ver - 1],n,f2,d2 + 1);
}
}
int main(){
n = read();
int m = read();
rootfa[0] = built_tree(1,0,n); //对并查集进行初始化,深度则不需要
for(int i = 1; i <= m; i ++){
int f = read();
if(f == 1){
int x = read(),y = read();
mer(x,i,y);
}
if(f == 2){
int a = read();
rootfa[i] = rootfa[a];
rootdep[i] = rootdep[a];
}
if(f == 3){
int x = read(),y = read();
rootfa[i] = rootfa[i - 1];
rootdep[i] = rootdep[i - 1];
int f1 = find(i,x);
int f2 = find(i,y);
if(f1 == f2) printf("1\n");
else printf("0\n");
}
}
return 0;
}