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. langchain:Prompt在手,天下我有

    目录 简介 好的prompt 什么是prompt template 在langchain中创建prompt template Chat特有的prompt template 总结 简介 prompts是 ...

  2. Nginx使用Lua脚本加解密RSA字符串

    本文主要介绍使用Lua脚本对采用RSA加密后的字符串进行解密的过程. 使用第三方类库lua-resty-rsa,参考地址:https://github.com/spacewander/lua-rest ...

  3. 将SQL从phpmyadmin导入到自己的Navicat

    数据库名称太有指向性了 容易把自己给干没了 一码得码的一塌糊涂 就不放图了 进入你的phpmyadmin 注意一定要好好的进到数据库里面 点击导出 如果提示是 正在导出数据库"{你的数据库名 ...

  4. quarkus实战之一:准备工作

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于<quarkus实战>系列 <q ...

  5. Spring-Bean(二)

    环境在spring-Bean(一)的配置下 https://www.cnblogs.com/doubleconquer/p/15603706.html 来查看ApplicationContext的执行 ...

  6. 【游记】NOI 2023省选游记

    Day -N 下午去机房训练,回去的时候邱大大说 "我\(,lj,szy\) 可以去参加 NOI 2023省选".我瞬间就惊了.作为一个 \(CSP-S\) 只有 \(91pts\ ...

  7. bash: pip3:未找到命令

    输入以下命令: 1 sudo apt-get install python3-pip 参考链接: https://www.cnblogs.com/banshaohuan/p/10963547.html

  8. Frida环境配置

    目录 安装Linux客户端 配置Android服务端 测试运行效果 官方手册 安装Linux客户端 github地址:https://github.com/frida/frida pip instal ...

  9. numpy 索引,切片 ,转置,变值,多个数组的拼接

  10. 「codeforces - 1608F」MEX counting

    link. 首先考虑暴力,枚举规划前缀 \([1, i]\) 和前缀 mex \(x\),则我们需要 \(x\) 个数来填了 \([0, x)\),还剩下 \(i-x\) 个数随便填 \([0, x) ...