bzoj 3673 可持久化并查集 by zky
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
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
Sample Output
1
0
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 + 10; 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+1,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+1,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+1,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+1,r,rs[rt]); } int fd(int x,int rt){ int pos = query(x,1,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(1,n,root[0]); for(int i = 1;i <= q;i ++){ scanf("%d",&op); if(op == 1){ scanf("%d%d",&x,&y); root[i] = root[i-1]; int fx = fd(x,root[i-1]); int fy = fd(y,root[i-1]); if(sum[fx] == sum[fy]) continue; if(dep[fx] > dep[fy]) swap(fx,fy); update(root[i-1],root[i],sum[fx],sum[fy],1,n); if(dep[fx] == dep[fy]) add(sum[fy],1,n,root[i]); } else if(op == 2){ scanf("%d",&k); root[i] = root[k]; } else { root[i] = root[i-1]; 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 0; }