bzoj 3673 可持久化并查集 by zky

时间:2023-03-08 20:19:17

Description

n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

0<n,m<=2*10^4

Input

Output

Sample Input

5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2

Sample Output

1
0
1

思路:

用主席树去维护一个可持久化的数组,并查集的操作就变成了在这个可持久化数组上跳来跳去,
连接两个点x,y就直接在主席树上下标为x点赋值为y,这样查询的时候只要一直跳就可以跳到根节点
实现代码;
#include<bits/stdc++.h>
using namespace std;
#define mid int m = (l + r) >> 1
const int M = 2e6 + ; int sum[M],ls[M],rs[M],dep[M],n,idx,root[M];
void build(int l,int r,int &rt){
rt = ++idx;
if(l == r){
sum[rt] = l;
return ;
}
mid;
build(l,m,ls[rt]); build(m+,r,rs[rt]);
return ;
} void update(int old,int &rt,int p,int c,int l,int r){
rt = ++idx; ls[rt] = ls[old]; rs[rt] = rs[old];
dep[rt] = dep[old];
if(l == r){
sum[rt] = c;
return ;
}
mid;
if(p <= m) update(ls[old],ls[rt],p,c,l,m);
else update(rs[old],rs[rt],p,c,m+,r);
} int query(int p,int l,int r,int rt){
if(l == r) return rt;
mid;
if(p <= m) return query(p,l,m,ls[rt]);
else return query(p,m+,r,rs[rt]);
} void add(int p,int l,int r,int rt){
if(l == r){
dep[rt] ++;
return;
}
mid;
if(p <= m) add(p,l,m,ls[rt]);
else add(p,m+,r,rs[rt]);
} int fd(int x,int rt){
int pos = query(x,,n,rt);
if(x == sum[pos]) return pos;
return fd(sum[pos],rt);
} int main()
{
int q,op,x,y,k;
scanf("%d%d",&n,&q);
build(,n,root[]);
for(int i = ;i <= q;i ++){
scanf("%d",&op);
if(op == ){
scanf("%d%d",&x,&y);
root[i] = root[i-];
int fx = fd(x,root[i-]);
int fy = fd(y,root[i-]);
if(sum[fx] == sum[fy]) continue;
if(dep[fx] > dep[fy]) swap(fx,fy);
update(root[i-],root[i],sum[fx],sum[fy],,n);
if(dep[fx] == dep[fy]) add(sum[fy],,n,root[i]);
}
else if(op == ){
scanf("%d",&k);
root[i] = root[k];
}
else {
root[i] = root[i-];
scanf("%d%d",&x,&y);
int fx = fd(x,root[i]);
int fy = fd(y,root[i]);
if(sum[fx] == sum[fy]) printf("1\n");
else printf("0\n");
}
}
return ;
}