可持久化并查集的两种写法

第一种(bzoj3673):

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


这道题可以保存操作,因为每次合并,改变的只有一个点的父亲,所以我们可以记录每次合并的两个点,当我们要回到历史版本时,只需令当前的等于历史版本的那个点即可。
具体实现我们可以建立一个类似链表的数据结构,保存t时刻的操作,即u和它的父亲v,last指向这次操作的上一次操作,如果是回到历史版本只需last指向历史值,即可。当我们每次找x的父亲节点时,就沿着last往回找,直到结束。
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<algorithm>
 7 #include<string>
 8 #include<map>
 9 #include<queue>
10 #include<vector>
11 #include<set>
12 #define inf 1000000000
13 #define maxn 20000+5
14 #define maxm 10000+5
15 #define eps 1e-10
16 #define ll long long
17 #define for0(i,n) for(int i=0;i<=(n);i++)
18 #define for1(i,n) for(int i=1;i<=(n);i++)
19 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
20 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
21 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
22 using namespace std;
23 int read(){
24     int x=0,f=1;char ch=getchar();
25     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
26     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
27     return x*f;
28 }
29 int n,m,cnt;
30 struct data{
31     int last,x,fx;
32 }fa[maxn];
33 int find(int now,int x){
34     if(!now)return x;
35     if(x!=fa[now].x)return find(fa[now].last,x);
36     return fa[now].fx;
37 }
38 int getfa(int x){
39     if(x==find(cnt,x))return x;
40     return getfa(find(cnt,x));
41 }
42 void merge(int x,int y){
43     int p=getfa(x),q=getfa(y);
44     if(p!=q)fa[cnt]=(data){cnt-1,q,p};
45 }
46 int query(int x,int y){
47     return getfa(x)==getfa(y)?1:0;
48 }
49 int main(){
50     //freopen("input.txt","r",stdin);
51     //freopen("output.txt","w",stdout);
52     n=read();m=read();
53     for1(i,m){
54         int x=read();
55         cnt++;
56         if(x==1){
57             int u=read(),v=read();
58             fa[cnt]=fa[cnt-1];
59             merge(u,v);
60         }
61         else if(x==2){
62             int u=read();
63             fa[cnt]=fa[u];
64         }
65         else{
66             int u=read(),v=read();
67             fa[cnt]=fa[cnt-1];
68             printf("%d\n",query(u,v));
69         }
70     }
71     return 0;
72 }
View Code

 

第二种(bzoj3674):

Description

Description:
自从zkysb出了可持久化并查集后……
hzwer:乱写能AC,暴力踩标程
KuribohG:我不路径压缩就过了!
ndsf:暴力就可以轻松虐!
zky:……

n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x = x xor lastans,lastans的初始值为0
0<n,m<=2*10^5


Input

 

Output

 

Sample Input

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

Sample Output

1
0
1

用可持久化线段树维护数组,叶子节点就是fa数组。

对于可持久化线段树,我们实际上是把每次修改点变成新增点,我们可以想到,每次修改,修改的是线段树上从根到叶子节点上的一条链,也就是每次修改最多增加logn个节点,所以空间复杂度并不会变得很差,同时我们会发现,实际上我们很多节点都是重复的,我们可以把新的点直接指向这些没用变动的点。

对于并查集的可持久化操作,实际上就是把原先直接访问数组的操作,变成了在线段树上单点查询,修改变成单点修改。

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<algorithm>
 7 #include<string>
 8 #include<map>
 9 #include<queue>
10 #include<vector>
11 #include<set>
12 #define inf 1000000000
13 #define maxn 200000+5
14 #define maxm 10000000+5
15 #define eps 1e-10
16 #define ll long long
17 #define for0(i,n) for(int i=0;i<=(n);i++)
18 #define for1(i,n) for(int i=1;i<=(n);i++)
19 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
20 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
21 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
22 using namespace std;
23 int read(){
24     int x=0,f=1;char ch=getchar();
25     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
26     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
27     return x*f;
28 }
29 int n,m,sz,last;
30 int root[maxn],ls[maxm],rs[maxm],v[maxm],deep[maxm];
31 void build(int &k,int l,int r){
32     if(!k)k=++sz;
33     if(l==r){v[k]=l;return ;}
34     int mid=(l+r)>>1;
35     build(ls[k],l,mid);
36     build(rs[k],mid+1,r);
37 }
38 void modify(int l,int r,int x,int &y,int pos,int val){
39     y=++sz;
40     if(l==r){v[y]=val;deep[y]=deep[x];return ;}
41     ls[y]=ls[x];rs[y]=rs[x];
42     int mid=(l+r)>>1;
43     if(pos<=mid)
44         modify(l,mid,ls[x],ls[y],pos,val);
45     else modify(mid+1,r,rs[x],rs[y],pos,val);
46 }
47 int query(int k,int l,int r,int pos){
48     if(l==r)return k;
49     int mid=(l+r)>>1;
50     if(pos<=mid)return query(ls[k],l,mid,pos);
51     else return query(rs[k],mid+1,r,pos);
52 }
53 void add(int k,int l,int r,int pos){
54     if(l==r){deep[k]++;return ;}
55     int mid=(l+r)>>1;
56     if(pos<=mid)add(ls[k],l,mid,pos);
57     else add(rs[k],mid+1,r,pos);    
58 }
59 int find(int k,int x){
60     int p=query(k,1,n,x);
61     if(x==v[p])return p;
62     return find(k,v[p]);
63 }
64 int main(){
65     //freopen("input.txt","r",stdin);
66     //freopen("output.txt","w",stdout);
67     n=read(),m=read();
68     build(root[0],1,n);
69     int f,k,a,b;
70     for1(i,m){
71         f=read();
72         if(f==1){
73             root[i]=root[i-1];
74             a=read();b=read();a=a^last;b=b^last;
75             int p=find(root[i],a),q=find(root[i],b);
76             if(v[p]==v[q])continue;
77             if(deep[p]>deep[q])swap(q,p);
78             modify(1,n,root[i-1],root[i],v[p],v[q]);
79             if(deep[p]==deep[q])add(root[i],1,n,v[q]);
80         }
81         else if(f==2){
82             k=read();k=k^last;root[i]=root[k];
83         }
84         else{
85             root[i]=root[i-1];
86             a=read();b=read();a=a^last;b=b^last;
87             int p=find(root[i],a),q=find(root[i],b);
88             if(v[p]==v[q])last=1;
89             else last=0;
90             printf("%d\n",last);
91         }
92     }
93     return 0;
94 }
View Code

 

posted @ 2016-07-04 18:36  HTWX  阅读(123)  评论(0编辑  收藏  举报