zoukankan      html  css  js  c++  java
  • BZOJ3673 & BZOJ3674 & 洛谷3402:可持久化并查集——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3673

    https://www.lydsy.com/JudgeOnline/problem.php?id=3674

    https://www.luogu.org/problemnew/show/P3402

    这里最强的应该是BZOJ3674,于是接下来讲的内容和代码与BZOJ3674相同而非另外两道题,但核心思维完全一致。

    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

    可持久化的你没见过的数据结构,都按照可持久化线段树来做准没错。(flag)

    用主席树记录历史版本,维护每个节点的爸爸和启发式合并所需要的并查集的深度。

    这样对于2操作只需要把版本号改了即可(程序中是rt[i]=rt[k])。

    原find操作可以暴力爬树一步步往上找,启发式合并深度为O(logn),主席树查询一次是O(logn),复杂度就是O(log^2n)。

    原union操作就相当于主席树的insert操作,比如fa[u]=v,我们只需要主席树找到代表u的点u0,然后fa[u0]=v即可。

    第三个操作有上面的基础就是傻逼操作了。

    #include<cstdio>
    #include<queue>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N=200001;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct tree{
        int l,r;
    }tr[N*20];
    int rt[N],fa[N*20],dep[N*20],n,m,pool;
    inline void build(int &x,int l,int r){
        x=++pool;
        if(l==r){
        fa[x]=l;
        return;
        }
        int mid=(l+r)>>1;
        build(tr[x].l,l,mid);
        build(tr[x].r,mid+1,r);
    }
    inline void insert(int y,int &x,int l,int r,int u,int v){
        tr[x=++pool]=tr[y];
        if(l==r){
        fa[x]=v;
        return;
        }
        int mid=(l+r)>>1;
        if(u<=mid)insert(tr[y].l,tr[x].l,l,mid,u,v);
        else insert(tr[y].r,tr[x].r,mid+1,r,u,v);
        return;
    }
    inline void add(int y,int &x,int l,int r,int p){
        if(l==r){
        dep[x]++;
        return;
        }
        int mid=(l+r)>>1;
        if(p<=mid)add(tr[y].l,tr[x].l,l,mid,p);
        else add(tr[y].r,tr[x].r,mid+1,r,p);
        return;
    }
    inline int query(int k,int l,int r,int p){
        if(l==r)return k;
        int mid=(l+r)>>1;
        if(p<=mid)return query(tr[k].l,l,mid,p);
        else return query(tr[k].r,mid+1,r,p);
    }
    inline int find(int k,int x){
        while(233){
        int f=query(rt[k],1,n,x);
        if(x==fa[f])return f;
        x=fa[f];
        }
    }
    inline void unionn(int k,int u,int v){
        if(dep[u]>dep[v])swap(u,v);
        insert(rt[k],rt[k],1,n,fa[u],fa[v]);
        if(dep[u]==dep[v])add(rt[k],rt[k],1,n,fa[v]);
    }
    int main(){
        n=read(),m=read();
        build(rt[0],1,n);
        int ans=0;
        for(int i=1;i<=m;i++){
        int op=read();
        if(op==1){
            rt[i]=rt[i-1];
            int a=find(i,read()^ans),b=find(i,read()^ans);
            if(a!=b)unionn(i,a,b);
        }
        if(op==2){
            int k=read()^ans;rt[i]=rt[k];
        }
        if(op==3){
            rt[i]=rt[i-1];
            int a=find(i,read()^ans),b=find(i,read()^ans);
            printf("%d
    ",ans=a==b?1:0);
        }
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    内部类
    多重继承关系初始化顺序及初始化
    String
    Error
    算法:插入排序
    算法:冒泡排序
    算法:选择排序
    注册Activity
    java变量的作用域和基本数据类型转换
    java数据类型
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8678125.html
Copyright © 2011-2022 走看看