P3402 可持久化并查集

题意:

给定n,表示有n个集合,m次操作,操作有三种:
(1,a,b):合并集合a和集合b
(2,k):回到第k次操作的状态
(3,a,b):查询a和b是否在同一集合

数据范围:n<=1e5,m<=2e5

解法:

将数组可持久化(用可持久化线段树实现),维护并查集每个节点的父节点pre和深度dep。
合并是按秩合并。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
int lc[maxm*40],rc[maxm*40];
int pre[maxm*40],dep[maxm*40];
int rt[maxm<<1],tot;
int n,m;
void build(int l,int r,int &k){
    k=++tot;
    if(l==r){
        pre[k]=l;
        dep[k]=0;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,lc[k]);
    build(mid+1,r,rc[k]);
}
void update(int x,int val,int l,int r,int last,int &k){
    k=++tot;
    lc[k]=lc[last],rc[k]=rc[last];
    if(l==r){
        pre[k]=val;
        dep[k]=dep[last];
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(x,val,l,mid,lc[last],lc[k]);
    else update(x,val,mid+1,r,rc[last],rc[k]);
}
int ask(int x,int l,int r,int k){//查找某版本下x的父亲
    if(l==r)return pre[k];
    int mid=(l+r)/2;
    if(x<=mid)return ask(x,l,mid,lc[k]);
    else return ask(x,mid+1,r,rc[k]);
}
void add(int x,int l,int r,int k){//将某版本下x的深度+1
    if(l==r){
        dep[k]++;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)add(x,l,mid,lc[k]);
    else add(x,mid+1,r,rc[k]);
}
int ffind(int x,int k){//查询某个版本下x的父亲
    int pre_x=ask(x,1,n,k);
    if(x==pre_x)return x;
    return ffind(pre_x,k);
}
signed main(){
    scanf("%d%d",&n,&m);
    build(1,n,rt[0]);
    for(int i=1;i<=m;i++){
        int op;scanf("%d",&op);
        if(op==1){//合并a,b
            int a,b;scanf("%d%d",&a,&b);
            rt[i]=rt[i-1];
            int x=ffind(a,rt[i]);
            int y=ffind(b,rt[i]);
            if(x==y)continue;
            if(dep[x]>dep[y])swap(x,y);//改成dep[x]<dep[y]
            update(x,y,1,n,rt[i-1],rt[i]);//把短的x接到长的y上
            if(dep[x]==dep[y])add(y,1,n,rt[i]);//如果dep[x]=dep[y],dep[y]需要+1
        }else if(op==2){//将版本置为rt[x]
            int x;scanf("%d",&x);
            rt[i]=rt[x];
        }else if(op==3){//判断a,b是否在同一个集合
            int a,b;scanf("%d%d",&a,&b);
            rt[i]=rt[i-1];
            int x=ffind(a,rt[i]);
            int y=ffind(b,rt[i]);
            if(x==y)puts("1");
            else puts("0");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107893130