zoukankan      html  css  js  c++  java
  • bzoj 3673: 可持久化并查集 by zky

    bzoj 3673: 可持久化并查集 by zky

    Time Limit: 5 Sec  Memory Limit: 128 MB

    Description

    n个集合 m个操作
    操作:
    1 a b 合并a,b所在集合
    2 k 回到第k次操作之后的状态(查询算作操作)
    3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

    0<n,m<=2*10^4

    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
    主席树+启发式合并+并查集
    以节点编号为下标,以祖先节点为权值建立主席树(也就是主席树内维护祖先节点是谁)
    并查集本就是支持合并的
    所谓启发式合并就是按秩合并
    即朴素的并查集合并时,谁合并谁是随机的
    按秩合并,秩即树的深度,深度小的树合并到深度大的树的上面
    所以我们要维护一个deep[]数组,记录每个集合的祖先节点的深度(根节点到最深的叶节点的距离)
    并查集合并时找到祖先节点,所以只有祖先节点的深度是有效的
    那么显然,只有合并两颗深度相同的树时才会更新deep
    查找祖先节点查找的比较巧妙
    利用主席树维护每个时刻的祖先节点
    将朴素的直接查询,改成先到主席树中找到这个节点编号的位置,返回这个位置存储的father
    对于回到历史版本,只需令主席树中起指示节点所在线段树位置的root[]=历史版本的root即可
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,cnt;
    int root[20001*20],lc[20001*20],rc[20001*20],fa[20001*20],deep[20001*20];
    void build(int & k,int l,int r)
    {
        k=++cnt;
        if(l==r) {fa[k]=l;return;}
        int mid=l+r>>1;
        build(lc[k],l,mid);
        build(rc[k],mid+1,r);
    }
    void change(int pre,int & now,int l,int r,int x,int y)
    {
        now=++cnt;
        if(l==r) 
        {
            fa[now]=x;
            deep[now]=deep[pre];
            return;
        }
        int mid=l+r>>1;
        if(y<=mid)
        {
            rc[now]=rc[pre];
            change(lc[pre],lc[now],l,mid,x,y);
        }
        else
        {
            lc[now]=lc[pre];
            change(rc[pre],rc[now],mid+1,r,x,y);
        }
    }
    int query(int y,int l,int r,int w)
    {
        if(l==r) return fa[y];
        int mid=l+r>>1;
        if(w<=mid) return query(lc[y],l,mid,w);
        else return query(rc[y],mid+1,r,w);
    }
    int find(int x,int i)
    {
        int p=query(root[i],1,n,x);
        if(p==x) return x;
        return find(p,i);
    }
    void update(int x,int l,int r,int w)
    {
        if(l==r) {deep[x]++;return;}
        int mid=l+r>>1;
        if(w<=mid) update(lc[x],l,mid,w);
        else update(rc[x],mid+1,r,w);
    }
    void unionn(int x,int y,int i)
    {
        if(deep[x]<deep[y]) swap(x,y);
        change(root[i-1],root[i],1,n,x,y);
        if(deep[x]==deep[y]) update(root[x],1,n,x);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        build(root[0],1,n);
        int x,y,z;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&z);
            if(z==1)
            {
                root[i]=root[i-1];
                scanf("%d%d",&x,&y);
                x=find(x,i);y=find(y,i);
                unionn(x,y,i);
            }
            else if(z==2)
            {
                scanf("%d",&x);root[i]=root[x];
            }
            else
            {
                scanf("%d%d",&x,&y);
                root[i]=root[i-1];
                x=find(x,i);y=find(y,i);
                printf("%d
    ",x==y);
            }
        }
    }
  • 相关阅读:
    toFixed()与银行家舍入
    VScode链接服务器并配置公钥-SSH Keys
    改造@vue/cli项目为服务端渲染-ServerSideRender
    vue预渲染及其cdn配置
    界面优化--如何提升用户体验(Velocity.js和GSAP)
    eslint配置介绍-如何在uniapp中配置eslint
    babel 的介绍及其配置
    如何为我的VUE项目编写高效的单元测试--Jest
    計算幾何 學習
    Manacher
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6527628.html
Copyright © 2011-2022 走看看