可持久化并查集总结

可持久化并查集总结

标签: 数据结构——可持久化——可持久化并查集
阅读体验:https://www.zybuluo.com/Junlier/note/1268670

前面的话

其实看起来很高大上,最后还不是沦为被我这种菜鸡都能学会的东西
嗯,我只是想解释这个并不是很难。。。连我这么弱都。。。
然后还是以模板题为背景将比较好。。。洛谷题目传送门

实现方法

其实可持久化并查集就是要支持回到以前的版本(类比可持久化$数组/线段树$之类的)
那么我都类比了,很显然就是用主席树来维护吧

主席树部分

我们以前学的主席树是不是可以支持让当前维护的数组回到之前的任一版本
那我们的并查集需要维护什么? 没错,$fa[]$数组
是不是我们知道了每一个版本的$fa[]$数组就相当于知道了这个版本的并查集?
那么问题转化成了用主席树维护一个$fa[]$数组对吧。。。
这个是主席树的板子

并查集部分

对于并查集,它的修改是合并两个独立的并查集
考虑如何合并
很显然不可以路径压缩(其实好像可以,只是我不会,反正这种方法也很优秀)
那么在不路径压缩的情况下怎么保证复杂度呢
嗯,普通的并查集有两种优化,路径压缩和按秩合并对吧
现在路径压缩用不了(我不会),那就考虑按秩合并(不会可以点一下上面的链接)
它可以把复杂度优化到log级别(因为树高控制了。。。)
那不就比较显然了。。。

总体来一发

  • 先建好一棵主席树,权值为$fa[i]=i$
  • 对于所有的询问:
  1. Update(修改):查询两点的父亲,如果不在一个并查集,看树高,小的合并向大的(按秩合并),合并的话就是直接主席树板子修改$fa[]$权值就ok
  2. Query(查询):这个直接查询,然后可持久化的版本转化就直接套版子做就行了

整体代码

我学的时候为了复习用写了注释
居然 派上用场了 ~(≧▽≦)/~啦啦啦

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<ctime>
#include<queue>
#include<stack>
#include<vector>
#define lst long long
#define ldb long double
#define N 200050
#define lson ljl[now].ls
#define rson ljl[now].rs
using namespace std;
const int Inf=1e9;
int read()
{
    int s=0,m=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}

int n,Q,tot;
int dep[N*30],fa[N*30];
int Edi[N];//版本编号
struct TREE{int ls,rs;}ljl[N*30];
//主席树部分
void Build(int &now,int le,int ri)
{
    if(!now)now=++tot;
    if(le==ri){fa[now]=le;return;}//并查集初始化
    int mid=(le+ri)>>1;
    Build(lson,le,mid),Build(rson,mid+1,ri);
}

void Update(int &now,int pre,int le,int ri,int loc,int ff)
//当前版本,上一个版本(复制时要用),左右端点,把loc的爸爸改成ff
{
    now=++tot;//新开log个节点
    if(le==ri)
    {
        dep[now]=dep[pre];
        //因为从前面的版本过来,所以树的深度也要继承
        fa[now]=ff;return;
    }
    lson=ljl[pre].ls,rson=ljl[pre].rs;//把前面的树“复制”过来
    int mid=(le+ri)>>1;
    if(loc<=mid)Update(lson,ljl[pre].ls,le,mid,loc,ff);
    else Update(rson,ljl[pre].rs,mid+1,ri,loc,ff);
}

int Query(int now,int le,int ri,int loc)
{//找到询问的节点记录的fa[]
    if(le==ri)return now;
    int mid=(le+ri)>>1;
    if(loc<=mid)return Query(lson,le,mid,loc);
    else return Query(rson,mid+1,ri,loc);
}
//嗯,属于可持久化并查集的特殊部分了
void add(int now,int le,int ri,int loc)
{//按秩合并的树高改变
    if(le==ri){dep[now]++;return;}
    int mid=(le+ri)>>1;
    if(loc<=mid)add(lson,le,mid,loc);
    else add(rson,mid+1,ri,loc);
}

int Find_fa(int edi,int now)
{
    int ff=Query(edi,1,n,now);//查询在这一版本里now的父亲
    if(now==fa[ff])return ff;
    return Find_fa(edi,fa[ff]);//无路径压缩
}

int main()
{
    n=read(),Q=read();
    Build(Edi[0],1,n);
    for(int i=1;i<=Q;++i)
    {
        int opt=read();
        if(opt==1)
        {
            Edi[i]=Edi[i-1];
            int x=read(),y=read();
            int fx=Find_fa(Edi[i],x),fy=Find_fa(Edi[i],y);
            if(fa[fx]==fa[fy])continue;
            if(dep[fx]>dep[fy])swap(fx,fy);//按秩合并,把x往y合并(dep小的往大的合并)
            Update(Edi[i],Edi[i-1],1,n,fa[fx],fa[fy]);
            if(dep[fx]+1>dep[fy])add(Edi[i],1,n,fa[fy]);
        }
        if(opt==2)
        {
            int kk=read();
            Edi[i]=Edi[kk];
        }
        if(opt==3)
        {
            Edi[i]=Edi[i-1];
            int x=read(),y=read();
            int fx=Find_fa(Edi[i],x),fy=Find_fa(Edi[i],y);
            if(fa[fx]==fa[fy])puts("1");
            else puts("0");
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjoierljl/p/9567859.html

时间: 2024-08-30 02:10:47

可持久化并查集总结的相关文章

可持久化并查集加强版 BZOJ 3674

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 3674: 可持久化并查集加强版 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 3225  Solved: 1192[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后--hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可

bzoj3674: 可持久化并查集

用可持久化线段树维护可持久化并查集. 调了一下午,改为按秩合并就过了... #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200100; const int INF=1e9+10; int n,m; int fa[ma

【BZOJ3674】可持久化并查集加强版

可持久化并查集我觉得就是可持久化数组的一种应用.可持久化数组,顾名思义,就是有历史版本的数组,那么如果我们暴力修改储存的话,修改O(n)查询O(1),空间O(n*m),这样肯定不可行,那么我们发现主席树有这样的功能,他可以快速复制,修改O(log),查询O(log),空间(m*log),是一个可行的方案.然后我们可持久化f数组维护fa,每次按照深度启发式合并,不进行路径压缩,这样能保证时间复杂度位单次O(log^2),空间复杂度为O(2*n+m*log).我不知道为什么不路径压缩,路径压缩是完全

bzoj3674 可持久化并查集加强版

Description 自从zkysb出了可持久化并查集后--hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:-- 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的初始值为00<n,m<=2*10^5 Samp

[bzoj3673][可持久化并查集 by zky] (rope(可持久化数组)+并查集=可持久化并查集)

Description n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output 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 Solution 用rope实现可持久化数组,用rope的历史记录功能实现可持久化并查集,通过时间168ms

【BZOJ-3673&amp;3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

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

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2084  Solved: 941[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

BZOJ 3674: 可持久化并查集加强版

3674: 可持久化并查集加强版 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 2605  Solved: 977[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状

3674: 可持久化并查集加强版

Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 3592  Solved: 1337[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询

半可持久化并查集

对于常见的可持久化并查集, 我们可以通过 按秩合并 + 可持久化数组 . 但是, 此半可持久化并查集, 非彼可持久化并查集. 我们不用支持时间旅行, 即不用支持回到过去的某个版本, 而只用储存历史信息. 举个例子来说吧. n 个点, 支持两种操作: ① 将某两个点之间连边; ② 查询在前 t 次操作下, 某两个点是否连通. 强制在线. 核心模型 给定 n 个点, m 条带权边的无向图. 求在所有边权不超过 d 的边的作用下, 某个点或某两个点的连通性信息. 强制在线. 之前举的那个例子也可以这样