可持久化专题(三)——可持久化并查集

前言

要学可持久化并查集,必须先会可持久化数组

L i n k Link Link

可持久化数组详见博客可持久化专题(二)——可持久化数组的实现


简介

可持久化并查集应该是一个挺实用的数据结构(例如NOI2018Day1T1中就有它的身影)。

它主要建立于可持久化数组的基础之上(而可持久化数组的实现是完全基于主席树的),因为这样就可以去访问一些历史版本从而实现可持久化了。

L i n k Link Link

主席树详见博客可持久化专题(一)——浅谈主席树:可持久化线段树


按秩合并

由于可持久化的缘故,我们要切记,可持久化并查集是不能像普通并查集一样写路径压缩的!

或许有人会问,不路径压缩,还不 T T T飞?

没关系,没有路径压缩,我们还有按秩合并,它的复杂度均摊是 O ( l o g n ) O(logn) O(logn)的,平时由于已经有路径压缩了,
所以基本上没人写按秩合并(实在没什么必要,时间复杂度优化并不大),而此时此刻,没法用路径压缩,按秩合并就起了很大的作用。

L i n k Link Link

按秩合并的复杂度证明详见博客启发式合并


具体实现

如果对可持久化并查集还有什么不懂的,最好再多思考一下,毕竟这还是有点深奥的。

下面是洛谷上可持久化并查集板子题的代码:(代码比可持久化数组长了许多)

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define LL long long
#define swap(x,y) (x^y?(x^=y,y^=x,x^=y):0)
#define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
#define N 200000 
int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
using namespace std;
int n,Q,tot=0,rt[N+5],a[N+5];
struct Chairman_Tree
{
   
   
    int Son[2],fa,level;
}node[N*20];
inline void read(int &x)
{
   
   
    x=0;int f<
### 可持久化并查集的实现方法与代码示例 可持久化并查集是一种支持历史版本查询的并查集结构,其核心操作包括合并两个集合、查询节点所在集合的祖先以及回溯到历史版本。由于路径压缩无法直接应用于可持久化数据结构,因此通常使用按合并的方式以保证操作复杂度为 \(O(\log n)\)[^5]。 以下是基于引用内容提供的可持久化并查集实现代码示例: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int f[MAXN], cnt[MAXN], sum[MAXN], id[MAXN], tot; // 查找节点 x 的祖先,并返回版本信息 int find(int x) { if (f[x] != x) return find(f[x]); return x; } // 初始化并查集 void init(int n) { for (int i = 0; i <= n; ++i) { f[i] = i; cnt[i] = 1; sum[i] = i; } tot = n; } // 合并两个集合 x 和 y inline void merge(int x, int y) { int fx = find(x), fy = find(y); if (fx == fy) return; if (cnt[fx] > cnt[fy]) swap(fx, fy); // 按合并 f[++tot] = fy; f[fx] = tot; cnt[fy] += cnt[fx]; sum[fy] += sum[fx]; } // 查询两点是否在同一集合 bool same_set(int x, int y) { return find(x) == find(y); } int main() { int n, q; cin >> n >> q; init(n); while (q--) { char op; int a, b; cin >> op >> a >> b; if (op == 'M') { // 合并操作 merge(a, b); } else if (op == 'Q') { // 查询操作 cout << (same_set(a, b) ? "Yes" : "No") << endl; } } return 0; } ``` 上述代码中,`merge` 函数实现了将两个节点所在的集合合并的功能,同时通过按合并优化了操作复杂度[^5]。`find` 函数用于查找节点的祖先,并支持历史版本的回溯[^2]。此外,`same_set` 函数用于判断两个节点是否属于同一集合[^4]。 ### 注意事项 - 在可持久化并查集中,路径压缩无法直接使用,因此需要依赖按合并来保证操作效率[^5]。 - 历史版本的查询可以通过维护额外的版本信息或利用时间戳实现[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值