【学习】fhq-treap
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
- 按照权值大小分裂
将原树 \(T\) 分成左右两个子树 \(T_l\), \(T_r\)。设当前节点为 \(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;
}
- 按照子树大小分裂
同理。
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\)。
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;
}
学习资料:
【学习】fhq-treap的更多相关文章
- fhq treap 学习笔记
序 今天心血来潮,来学习一下fhq treap(其实原因是本校有个OIer名叫fh,当然不是我) 简介 fhq treap 学名好像是"非旋转式treap及可持久化"...听上去怪 ...
- FHQ treap学习(复习)笔记
.....好吧....最后一篇学习笔记的flag它倒了..... 好吧,这篇笔记也鸽了好久好久了... 比赛前刷模板,才想着还是补个坑吧... FHQ,这个神仙(范浩强大佬),发明了这个神仙的数据结构 ...
- 「FHQ Treap」学习笔记
话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...
- 「学习笔记」 FHQ Treap
FHQ Treap FHQ Treap (%%%发明者范浩强年年NOI金牌)是一种神奇的数据结构,也叫非旋Treap,它不像Treap zig zag搞不清楚(所以叫非旋嘛),也不像Splay完全看不 ...
- Fhq Treap [FhqTreap 学习笔记]
众所周知 Fhq Treap 是 fhq 神仙研究出来的平衡树- 具体实现 每个点实现一个 \(\text{rnd}\) 表示 rand 的值 为什么要 rand 呢 是为了保证树高为 \(\log ...
- fhq treap最终模板
新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...
- 在平衡树的海洋中畅游(四)——FHQ Treap
Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...
- 平衡树合集(Treap,Splay,替罪羊,FHQ Treap)
今天翻了翻其他大佬的博客,发现自己有些...颓废... 有必要洗心革面,好好学习 序:正常的BST有可能退化,成为链,大大降低效率,所以有很多方法来保持左右size的平衡,本文将简单介绍Treap,S ...
- 【数据结构】平衡树splay和fhq—treap
1.BST二叉搜索树 顾名思义,它是一棵二叉树. 它满足一个性质:每一个节点的权值大于它的左儿子,小于它的右儿子. 当然不只上面那两种树的结构. 那么根据性质,可以得到该节点左子树里的所有值都比它小, ...
- NOI 2002 营业额统计 (splay or fhq treap)
Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...
随机推荐
- 从GaussDB(DWS)的技术演进,看数据仓库的积淀与新生
摘要:随着云计算的兴起和渗透,云数仓成为了数仓技术演进的新阶段,并且逐渐成为了众多企业的共同选择. 本文分享自华为云社区<从GaussDB(DWS)的技术演进,看数据仓库的积淀与新生>,作 ...
- 【SpringBoot】定时任务
SpringBoot实现定时任务 SpringBoot创建定时任务,目前主要有以下三种实现方式: 基于注解(@Scheduled): 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执 ...
- [最长回文字符串]manacher马拉车
manacher马拉车 https://www.luogu.com.cn/problem/P3805 闲言一下:花了一个中午终于把 manacher 给搞懂了.本文将以一个蒟蒻的身份来,来写写马拉车算 ...
- 【EF Core】主从实体关系与常见实体关系的区别
上次老周扯了有关主.从实体的话题,本篇咱们再挖一下,主.从实体之间建立的关系,跟咱们常用的一对一.一对多这些关系之间有什么不同. 先看看咱们从学习数据库开始就特熟悉的常用关系--多对多.一对一.一对多 ...
- 揭秘 .NET 中的 TimerQueue(上)
前言 TimerQueue 是.NET中实现定时任务的核心组件,它是一个定时任务的管理器,负责存储和调度定时任务.它被用于实现很多 .NET 中的定时任务,比如 System.Threading.Ti ...
- 三级缓存---解决 Spring 循环依赖
1. 循环依赖 1.1 什么是循环依赖 首先,什么是循环依赖?这个其实好理解,就是两个 Bean 互相依赖,类似下面这样: """ @Service public cla ...
- 阿里如何实现秒级百万TPS?搜索离线大数据平台架构解读
★ 淘宝搜索阶段 在2008-2012这个阶段,我们重点支持淘宝搜索的业务发展,随着淘宝商品量的不断增加,逐步引入Hadoop.Hbase等开源大数据计算和存储框架,实现了搜索离线系统的分布式化,有力 ...
- AcWing 第 92 场周赛 C题 4866. 最大数量 题解
原题链接 链表 + 并查集乱搞做法: 思路 首先可以发现,想要让度数尽量大,那我们应该构造成菊花图,即下图所示: 对于每个需求,我们可以知道,如果之前他们没有连在一起,那我们一定得把他们连在一起,该过 ...
- 【Nacos篇】Nacos基本操作及配置
官方文档:https://nacos.io/zh-cn/docs/v2/ecology/use-nacos-with-spring-cloud.html 前置条件:SpringCloud脚手架 单机模 ...
- 如何让WPF中的ValidationRule实现参数绑定
背景 应用开发过程中,常常会对用户输入内容进行验证,通常是基于类型.范围.格式或者特定的要求进行验证,以确保输入符合预期.例如邮箱输入框校验输入内容是否符合邮箱格式.在WPF中,数据模型允许将Vali ...