fhq-treap 是一种好写、复杂度低,且功能的优秀数据结构,涵盖了 treap 几乎所有的功能,其巧妙之处,就在于运用分离和合并两种操作代替了旋转操作。

1. BST 的定义

(摘自 OI Wiki)二叉搜索树(BST)是一种二叉树的树形数据结构,其定义如下:

  • 空树是 BST
  • 若 BST 左子树不为空,则其左子树上所有点的附加权值均小于根节点。
  • 若 BST 右子树不为空,则其右子树上所有点的附加权值均大于根节点。
  • BST 的左右子树均为 BST。

2. fhq-treap

2.1 需要存储的信息

左儿子 ls,右儿子 rs,子树大小 sz,权值 val 维护 tree 的性质,及随机优先级 key 维护 heap 的性质。

大体同 treap 相同,但 fhq-treap 不需要记录 val 重复次数 cnt,因为一般情况下,它不将权值相同节点视作一个节点。

2.2 更新答案 sz[x] = sz[ls[x]] + sz[rs[x]] + 1;
2.3 基础操作:分裂、合并与新建节点
2.3.1 分裂 split
  1. 按照权值大小分裂

将原树 \(T\) 分成左右两个子树 \(T_l\), \(T_r\)。设当前节点为 \(p\):

\[1':\ val_p\leq v, p\in T_l,\ split\ rs_p\\
2':\ val_p> v, p\in T_r,\ split\ ls_p
\]

当 \(p=0\) 时停止 split。

void split(int p, int v, int &x, int &y) {
if (!p) {x = y = 0; return ;}
if (val[p] <= v) x = p, split(rs[p], v, rs[x], y);
else y = p, split(ls[p], v, x, ls[y]);
sz[p] = sz[ls[p]] + sz[rs[p]] + 1;
}
  1. 按照子树大小分裂

同理。

void split(int p, int v, int &x, int &y) {
if (!p) {x = y = 0; return ;}
if (v <= sz[ls[p]]) x = p, split(rs[p], v, rs[x], y);
else y = p, split(ls[p], v, x, ls[y]);
sz[p] = sz[ls[p]] + sz[rs[p]] + 1;
}
2.3.2 合并 merge

条件:合并的两棵树 \(T_x\) 和 \(T_y\) 必须满足一棵严格小于另一棵,此处设 \(T_x<T_y\)。

\[1': key_x>key_y, fa_y=x, rs_x\leftarrow merge(rs_x,y)
2': key_x\leq key_y, fa_x=y, ls_y\leftarrow merge(x,ls_y)
\]

当 \(x,y\) 不全非空停止 merge,使用类似线段树合并 trick,返回 \(x \or y\)

int merge(int x, int y) {
if (!x || !y) return x | y;
if (key[x] < key[y]) {
ls[y] = merge(x, ls[y]), sz[y] = sz[ls[y]] + sz[rs[y]] + 1;
return y;
} else {
rs[x] = merge(rs[x], y), sz[x] = sz[ls[x]] + sz[rs[x]] + 1;
reutrn x;
}
}
2.3.1 新建节点
int nd(int v) {
int x = node++;
val[x] = v, rd[x] = rand(), sz[x] = 1;
return x;
}
2.4 更多基础功能

见普通平衡树模板题 P3369 【模板】普通平衡树

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, inf = 1e9 + 7;
int n, m, rt, node, ls[N], rs[N], val[N], rd[N], sz[N];
void push(int x) {sz[x] = sz[ls[x]] + sz[rs[x]] + 1;}
void spl(int p, int v, int &x, int &y) {
if (!p) {x = y = 0; return ;}
if (val[p] <= v) x = p, spl(rs[p], v, rs[x], y);
else y = p, spl(ls[p], v, x, ls[y]);
push(p);
}
int mer(int x, int y) {
if (!x || !y) return x | y;
if (rd[x] < rd[y]) {
ls[y] = mer(x, ls[y]), push(y);
return y;
} else {
rs[x] = mer(rs[x], y), push(x);
return x;
}
}
int nd(int v) {
int x = ++node;
val[x] = v, rd[x] = rand(), sz[x] = 1;
return x;
}
void ins(int v) {
int x = 0, y = 0;
spl(rt, v - 1, x, y), rt = mer(mer(x, nd(v)), y);
}
void del(int v) {
int x = 0, y = 0, z = 0;
spl(rt, v, x, z), spl(x, v - 1, x, y);
y = mer(ls[y], rs[y]), rt = mer(mer(x, y), z);
}
int kth(int k) {
int p = rt;
while (1) {
if (k <= sz[ls[p]]) p = ls[p];
else if (k == sz[ls[p]] + 1) return val[p];
else k -= sz[ls[p]] + 1, p = rs[p];
}
}
int pre(int v){
int p = rt, ans = 0;
while (1) {
if (!p) return ans;
else if (v <= val[p]) p = ls[p];
else ans = val[p], p = rs[p];
}
}
int suc(int v) {
int p = rt, ans = 0;
while (1) {
if (!p) return ans;
else if (v >= val[p]) p = rs[p];
else ans = val[p], p = ls[p];
}
}
int rnk(int v) {
int x = 0, y = 0, ans = 0;
spl(rt, v - 1, x, y), ans = sz[x] + 1;
return rt = mer(x,y), ans;
}
int main(){
cin >> n;
while (n--) {
int op, x;
cin >> op >> x;
if (op == 1) ins(x);
else if (op == 2) del(x);
else if (op == 3) cout << rnk(x) << endl;
else if (op == 4) cout << kth(x) << endl;
else if (op == 5) cout << pre(x) << endl;
else cout << suc(x) << endl;
}
return 0;
}

学习资料:

  1. fhq-treap by Alex_Wei

  2. fhq-treap by 粉兔

  3. OI Wiki

【学习】fhq-treap的更多相关文章

  1. fhq treap 学习笔记

    序 今天心血来潮,来学习一下fhq treap(其实原因是本校有个OIer名叫fh,当然不是我) 简介 fhq treap 学名好像是"非旋转式treap及可持久化"...听上去怪 ...

  2. FHQ treap学习(复习)笔记

    .....好吧....最后一篇学习笔记的flag它倒了..... 好吧,这篇笔记也鸽了好久好久了... 比赛前刷模板,才想着还是补个坑吧... FHQ,这个神仙(范浩强大佬),发明了这个神仙的数据结构 ...

  3. 「FHQ Treap」学习笔记

    话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...

  4. 「学习笔记」 FHQ Treap

    FHQ Treap FHQ Treap (%%%发明者范浩强年年NOI金牌)是一种神奇的数据结构,也叫非旋Treap,它不像Treap zig zag搞不清楚(所以叫非旋嘛),也不像Splay完全看不 ...

  5. Fhq Treap [FhqTreap 学习笔记]

    众所周知 Fhq Treap 是 fhq 神仙研究出来的平衡树- 具体实现 每个点实现一个 \(\text{rnd}\) 表示 rand 的值 为什么要 rand 呢 是为了保证树高为 \(\log ...

  6. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...

  7. 在平衡树的海洋中畅游(四)——FHQ Treap

    Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...

  8. 平衡树合集(Treap,Splay,替罪羊,FHQ Treap)

    今天翻了翻其他大佬的博客,发现自己有些...颓废... 有必要洗心革面,好好学习 序:正常的BST有可能退化,成为链,大大降低效率,所以有很多方法来保持左右size的平衡,本文将简单介绍Treap,S ...

  9. 【数据结构】平衡树splay和fhq—treap

    1.BST二叉搜索树 顾名思义,它是一棵二叉树. 它满足一个性质:每一个节点的权值大于它的左儿子,小于它的右儿子. 当然不只上面那两种树的结构. 那么根据性质,可以得到该节点左子树里的所有值都比它小, ...

  10. NOI 2002 营业额统计 (splay or fhq treap)

    Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...

随机推荐

  1. go NewTicker 得使用

    转载请注明出处: 在 Go 语言中,time.NewTicker 函数用于创建一个周期性触发的定时器.它会返回一个 time.Ticker 类型的值,该值包含一个通道 C,定时器会每隔一段时间向通道 ...

  2. Python中 sys.argv[]用法详解

    sys.argv[0]表示代码本身文件路径. sys.argv[]是一个从程序外部获取参数的桥梁,从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可以看作是一 ...

  3. 基于JavaFX的扫雷游戏实现(五)——设置和自定义控件

      它来了它来了,最后一期终于来了.理论上该讲的全都讲完了,只剩下那个拖了好几期的自定义控件和一个比较没有存在感的设置功能没有讲.所以这次就重点介绍它们俩吧.   首先我们快速浏览下设置的实现,上图: ...

  4. Blazor资源大全,很棒的Blazor

    Blazor资源大全 一个收集了很棒的Blazor资源的集合. Blazor是一个使用C#/Razor和HTML在浏览器中运行的.NET Web框架. 欢迎贡献!请先查看贡献指南.感谢所有的贡献者,你 ...

  5. Redis的设计与实现(6)-压缩列表

    压缩列表 (ziplist) 是列表键和哈希键的底层实现之一. 当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列 ...

  6. Blazor前后端框架Known-V1.2.6

    V1.2.6 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Gith ...

  7. 关于 yield 关键字(C#)

    〇.前言 yield 关键字的用途是把指令推迟到程序实际需要的时候再执行,这个特性允许我们更细致地控制集合每个元素产生的时机. 对于一些大型集合,加载起来比较耗时,此时最好是先返回一个来让系统持续展示 ...

  8. 【游记】NOIP2022 预备赛游记

    Day -2 \(NOIP\) 就要来了,\(CSYZ\) 斥巨资给我们在 \(NOIP\) 正式考点举办了一场 \(NOIP\) 预备赛,真是太感动了~~ \(cy\) 说明天要颁奖,激动激动! D ...

  9. github.com/yuin/gopher-lua 踩坑日记

    本文主要记录下在日常开发过程中, 使用 github.com/yuin/gopher-lua 过程中需要注意的地方. 后续遇到其他的需要注意的事项再补充. 1.加载LUA_PATH环境变量 在实际开发 ...

  10. api接口的使用原理是什么?

    ​ 随着互联网的发展和不同系统之间的交互越来越频繁,API接口的使用已经成为软件开发和集成中不可或缺的一部分.API接口的使用原理是通过预定义的接口规范,软件系统可以调用或提供API接口的服务,来实现 ...