可持久化并查集(知识整理+板子总结)

概念

学了可持续化数组之后,趁热打铁拿下可持久化并查集

理解之后就大致是当抄板子用,毕竟手敲还要敲很久

不得不承认,此生不如NOI爷,只愿自己能做一个努力种技能树的ACMer

 

可持久化并查集=可持久化数组+叶子结点维护并查集par[]关系和dep[]关系+并查集合并

 

dep[rt]是从rt这个节点往下搜的最大链长,其实也就是以rt为树根的树的最深的叶子的深度

dep[]数组用于按秩合并,秩这个字,不用太纠结,直接叫它按链长合并可能更通俗一些

按秩合并,就是按dep[]数组的值合并,或者平时并查集union里常用Rank[]数组,含义是一样的

这么做,是让矮树的树根接到高树的树根上去,尽量防止树的退化

 

每个叶子结点now维护的是这个点的位置的pos的父亲和秩,

但是par[now]=pos2记录的是父亲的位置,秩记录的是在当前状态下pos所在的树

由于秩只在以后树的修改会用到,在查询的时候只会查询par[]的值

所以每个叶子结点只记录一个当前秩即可,无需维护历史值

查询值的时候返回叶子结点编号now,便于对该节点的信息进行修改

 

按秩合并

每个点上的值是这个点的当前的秩,x的秩<=y的秩

①x的秩<y的秩

合并x和y时,直接把x的根连到y的根上即可

②x的秩==y的秩

合并x和y的时候,把x的根连到y的根上之后,再对y的根的秩加一

y的子树错了不用管,合并的时候只会用到根的秩,保证其正确即可

一些函数的说明

build函数,建树,现用现开点,叶子结点维护的par值是其父亲的pos

merge函数,合并过程第一部分,新开一条链,并将链的叶子结点的par值修改,dep值沿用历史版本

find函数,并查集找祖先,无路径压缩,返回的是祖先的pos所在叶子结点的标号now,且此刻par[now]就是pos

update函数,合并过程的第二部分(x秩==y秩时),对y的新版本的链的叶子结点的dep值+1

query函数,返回代表pos位置的叶子结点的标号

恢复为历史版本x,root[i]=root[x]

更新一个版本,新建一个版本root[i],先沿用(恢复为)上一个版本root[i-1],再调用merge函数对root[i]进行新建结点操作

思路来源

https://www.cnblogs.com/peng-ym/p/9357220.html

心得

博主的讲解风格清晰,代码风格也是好评,NOI爷就是强鸭……

局部静态变量static int避免了多次开浪费内存;

命名空间namespace的使用避免了某些定义的冲突,封装性的体现

感觉自己背着敲也敲不出来,就套人家板子叭……

思路来源代码

#include<bits/stdc++.h>
#define N 301000
using namespace std;
template<typename T>inline void read(T &x)
{
    x=0;
    static int p;p=1;
    static char c;c=getchar();
    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
    x*=p;
}
int n,m;
int L[N*30],R[N*30],fa[N*30],dep[N*30];
int root[N*30];
namespace Persistant_Union_Set
{
#define Mid ((l+r)>>1)
#define lson L[rt],l,Mid
#define rson R[rt],Mid+1,r
    int cnt;
    void build(int &rt,int l,int r)
    {
        rt=++cnt;
        if(l==r){fa[rt]=l;return ;}
        build(lson);build(rson);
    }
    void merge(int last,int &rt,int l,int r,int pos,int Fa)
    {
        rt=++cnt;L[rt]=L[last],R[rt]=R[last];
        if(l==r)
        {
            fa[rt]=Fa;
            dep[rt]=dep[last];
            return ;
        }
        if(pos<=Mid)merge(L[last],lson,pos,Fa);
        else merge(R[last],rson,pos,Fa);
    }
    void update(int rt,int l,int r,int pos)
    {
        if(l==r){dep[rt]++;return ;}
        if(pos<=Mid)update(lson,pos);
        else update(rson,pos);
    }
    int query(int rt,int l,int r,int pos)
    {
        if(l==r)return rt;
        if(pos<=Mid)return query(lson,pos);
        else return query(rson,pos);
    }
    int find(int rt,int pos)
    {
        int now=query(rt,1,n,pos);
        if(fa[now]==pos)return now;
        return find(rt,fa[now]);
    }
#undef Mid
#undef lson
#undef rson
}
using namespace Persistant_Union_Set;
int main()
{
    read(n);read(m);
    build(root[0],1,n);
    for(int i=1;i<=m;i++)
    {
        static int opt,x,y;
        read(opt);read(x);
        if(opt==1)
        {
            read(y);
            static int posx,posy;
            root[i]=root[i-1];
            posx=find(root[i],x);posy=find(root[i],y);
            if(fa[posx]!=fa[posy])
            {
                if(dep[posx]>dep[posy])swap(posx,posy);
                merge(root[i-1],root[i],1,n,fa[posx],fa[posy]);
                if(dep[posx]==dep[posy])update(root[i],1,n,fa[posy]);
            }
        }
        else if(opt==2)root[i]=root[x];
        else if(opt==3)
        {
            read(y);
            root[i]=root[i-1];
            static int posx,posy;
            posx=find(root[i],x);posy=find(root[i],y);
            if(fa[posx]==fa[posy])puts("1");
            else puts("0");
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值