【bzoj3673】可持久化并查集 by zky
Time Limit: 5 Sec Memory Limit: 128 MB
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
用可持久化线段树来可持久化数组以达到可持久化并查集的作用,有点像绕口令。。。
要注意的就是并查集的find也要在树上找到那个位置,使用它的位置,所以复杂度应该是两个log的,n logn logn 一次AC
1 #include<bits/stdc++.h> 2 #define maxn 20005 3 #define mae 800005 4 using namespace std; 5 int n,m,root[maxn],l[mae],r[mae],cnt,fa[mae],rank[mae],L[mae],R[mae]; 6 void build(int& k,int le,int ri){ 7 if(!k) k=++cnt;L[k]=le;R[k]=ri; 8 if(le==ri){fa[k]=le;rank[k]=0;return;} 9 int mid=le+ri>>1; 10 build(l[k],le,mid);build(r[k],mid+1,ri); 11 } 12 void newly(int &k,int wi,int f,int h){ 13 k=++cnt; 14 L[k]=L[wi];R[k]=R[wi]; 15 if(L[k]==R[k]){fa[k]=h;return;} 16 l[k]=l[wi];r[k]=r[wi]; 17 int mid=L[k]+R[k]>>1; 18 if(f<=mid) newly(l[k],l[wi],f,h); 19 else newly(r[k],r[wi],f,h); 20 } 21 int find(int k,int x){ 22 if(L[k]==R[k]) return k; 23 int mid=L[k]+R[k]>>1; 24 if(x<=mid) return find(l[k],x); 25 else return find(r[k],x); 26 } 27 int fi(int x,int k){ 28 k=find(root[x],k); 29 if(fa[k]!=L[k]) return fi(x,fa[k]); 30 return fa[k]; 31 } 32 int main(){ 33 scanf("%d%d",&n,&m); 34 build(root[0],1,n);int a,b,c,f,h; 35 for(int i=1;i<=m;i++){ 36 scanf("%d",&a); 37 if(a==1){ 38 scanf("%d%d",&b,&c); 39 f=fi(i-1,b);h=fi(i-1,c); 40 if(rank[f]<rank[h]) newly(root[i],root[i-1],f,h); 41 else{ 42 if(rank[f]>rank[h]) newly(root[i],root[i-1],h,f); 43 else {newly(root[i],root[i-1],f,h);rank[find(root[i],f)]++;} 44 } 45 } 46 if(a==2) scanf("%d",&b),root[i]=root[b]; 47 if(a==3){ 48 scanf("%d%d",&b,&c);root[i]=root[i-1]; 49 f=fi(i,b);h=fi(i,c); 50 if(f==h) printf("1\n"); 51 else printf("0\n"); 52 } 53 } 54 return 0; 55 }