zoukankan      html  css  js  c++  java
  • [模板] 可持久化并查集

    ## 题目描述

    n个集合 m个操作

    操作:

    • 1 a b 合并a,b所在集合

    • 2 k 回到第k次操作之后的状态(查询算作操作)

    • 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

    输入样例#1:

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

    输出样例#1:

    1
    0
    1
    

    说明

    [1≤n≤10^5,1≤m≤2*10^5 ]

    By zky 出题人大神犇

    Solution

    最近几天我看到2018NOI day1 T1 可以用可持化并查集做,我最喜欢可持久化数据结构了,于是就去学了一波。
    可持久化并查集利用的是可持久化数组+按秩合并并查集(好像不可以路路径压缩),可持久化数组不会的话可以看我的另一篇博客可持久化数组。我们用数组fa[]来存一个节点的父亲,而不是祖先。dep[]数组存的是当前并查集的深度,因为要按秩合并。剩下的在代码中解释,不要在意我的中文式打法。

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int root[201000],fa[10001000],tot,dep[10000010],n;
    struct TREE
    {
        int ln,rn;
    }t[10010000];//主席树
    void build(int &node,int l,int r)
    {
        node=++tot;
        if(l==r) {fa[node]=l;dep[node]=1;return;}
        int mid=(l+r)/2;
        build(t[node].ln,l,mid);
        build(t[node].rn,mid+1,r);
    }
    int find(int node,int l,int r,int x)
    {
        if(l==r) return node;
        int mid=(l+r)/2;
        if(x<=mid) return find(t[node].ln,l,mid,x);
        else return find(t[node].rn,mid+1,r,x);
    }
    int getfa(int node,int x)
    {
        int p=find(node,1,n,x);
        if(fa[p]==x) return p;
        else return getfa(node,fa[p]);
    }
    void gai(int &node,int last,int l,int r,int x,int y)
    {
        node=++tot;
        if(l==r) {fa[node]=y;dep[node]=dep[last];return;}
        int mid=(l+r)/2;
        t[node]=t[last];
        if(x<=mid) gai(t[node].ln,t[last].ln,l,mid,x,y);
        else gai(t[node].rn,t[last].rn,mid+1,r,x,y);
    }
    void xiu(int node,int l,int r,int x)
    {
        if(l==r) {dep[node]++;return;}
        int mid=(l+r)/2;
        if(x<=mid) xiu(t[node].ln,l,mid,x);
        else xiu(t[node].rn,mid+1,r,x);
    }
    void merge(int x,int y,int i)//合并
    {
        if(dep[x]>dep[y]) swap(x,y);//按秩合并
        gai(root[i],root[i-1],1,n,fa[x],fa[y]);
        if(dep[x]==dep[y]) xiu(root[i],1,n,fa[y]);//深度一样,修改父节点深度
    }
    int main()
    {
        int m,opt,x,y;
        cin>>n>>m;
        build(root[0],1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&opt);
            root[i]=root[i-1];
            if(opt==1)
            {
                scanf("%d%d",&x,&y);
                x=getfa(root[i],x);y=getfa(root[i],y);
                if(fa[x]!=fa[y]) merge(x,y,i);
            }
            if(opt==2)
            scanf("%d",&x),root[i]=root[x];
            if(opt==3)
            {
                scanf("%d%d",&x,&y);
                x=getfa(root[i],x);y=getfa(root[i],y);
                if(fa[x]!=fa[y]) printf("0
    ");
                else printf("1
    ");
            }
        }
    }
    

    学完之后就差不多可以打2018NOI归程了。

  • 相关阅读:
    C++格式化输入输出
    算法的时间复杂度和空间复杂度
    C++编程中const和#define的区别
    C#中结构体和类的区别
    SQL之删除触发器
    Windows添加和取消右键管理员权限
    SQL之trigger(触发器)
    SQL VIEW(视图)
    二分查找的实现
    C++中this指针
  • 原文地址:https://www.cnblogs.com/kzj-pwq/p/9376080.html
Copyright © 2011-2022 走看看