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

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 + 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;
}

 

posted @ 2018-11-06 21:48  冥想选手  阅读(153)  评论(0编辑  收藏  举报