可持久化并查集
可持久化并查集 = 可持久化 + 并查集 = 主席树 + 并查集
leetcode 1724 检查边长度限制的路径是否存在 II
主席树方便用来维护历史的版本信息,可以支持回退, 访问之前版本的数据结构。
并查集,常用的两种优化方法是路径压缩,按秩合并。在可持久化并查集中只会用到按秩合并,即深度小的点向深度大的点合并,保证单次查询的时间复杂度为$ologn$
- 可持久化并查集进本操作,状态定义
1 | int tot; |
建立基础主席树
1
2
3
4
5
6
7
8
9
10void build(int &t, int l, int r) {
t = ++tot;
if (l == r) {
fa[t] = l;
return;
}
int mid = l + (r - l) / 2;
build(ls[t], l, mid);
build(rs[t], mid + 1, r);
}查找某一个元素在主席树中的下标
1
2
3
4
5
6
7int query(int t, int l, int r, int x) {
// t为某一历史版本的根节点下标, x为待查找的元素
if (l == r) return t;
int mid = l + (r - l) / 2;
if (x <= mid) return query(ls[t], l, mid, x);
else return query(rs[t], mid + 1, r, x);
}查找版本号为t元素x的祖先
1
2
3
4
5int find(int t, int l, int r, int x) {
int p = query(t, l, r, x); // 查询x的下标
if (fa[p] == x) return x;
return find(t, l, r, fa[p]);
}按秩合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14int unite(int pre, int l, int r, int x, int father) {
// 修改x节点的父亲为father
int t = ++tot;
ls[t] = ls[pre], rs[t] = rs[pre];
if (l == r) {
fa[t] = father;
sz[t] = sz[pre];
return t;
}
int mid = l + (r - l) / 2;
if (x <= mid) ls[t] = unite(ls[pre], l, mid, x, father);
else rs[t] = unite(rs[pre], mid + 1, r, x, father);
return t;
}更新合并后父节点的深度
1
2
3
4
5
6
7
8
9void update(int t, int l, int r, int x) {
if (l == r) {
sz[t]++;
return;
}
int mid = l + (r - l) / 2;
if (x <= mid) update(ls[t], l, mid, x);
else update(rs[t], mid + 1, r, x);
}全部代码如下, java用时329ms通过了全部测试用例, 但是用c++超时了…..不开o2优化太坑人了。
1 |
|