浅谈 FHQ Treap
浅谈 FHQ Treap
简单介绍
FHQ Treap,以下简写为
优点是比较好理解、代码短、上手快、可持久化
主要是不用像
Treap比较好玩的一点是,它整体是拥有二叉搜索树的性质,但是它的每一个节点都会有一个附加权值,它的附加权值是符合堆的性质。
这样我们可以明显看出,这棵树的形态(平衡与否)是由这个附加权值决定的。
那我们该如何取这个权值呢?随机!!
是的,我们每次都随机一个权值作为它的附加权值,那么它就大概是平衡的。
只需要两个基础操作,就可以达到splay的所有功能
前置操作
结构
对于每个树上节点开一个结构体,维护左儿子、右儿子、两个权值、还有子树中的节点个数。
然后给定一个树上的权值
struct fhq {
int l , r , v , t , sz;
} tr[N];
void update (int x) { tr[x].sz = 1 + tr[tr[x].l].sz + tr[tr[x].r].sz; }
int new_pos (int v) {
tr[++pos].sz = 1;
tr[pos].v = v;
tr[pos].t = rnd ();
return pos;
}
std::mt19937 rnd(233);
分裂 split
这个的主要思想就是把一个
把所有小于等于
可以对照代码感性理解一下
void split (int now , int k , int &x , int &y) {
if (now == 0) x = y = 0;
else {
if (tr[now].v <= k)
x = now , split (tr[now].r , k , tr[now].r , y);
else
y = now , split (tr[now].l , k , x , tr[now].l);
update (now);
}
}
合并 merge
图中黄字为堆权
int merge (int x , int y) {
if (x == 0 || y == 0) return x + y;
if (tr[x].t < tr[y].t) {
tr[x].r = merge (tr[x].r , y);
update (x);
return x;
}
else {
tr[y].l = merge (x , tr[y].l);
update (y);
return y;
}
}
一般操作
Insert
插入一个权值为
split (rt , a , x , y);
rt = merge (merge (x , new_pos (a)) , y);
delete
删除权值为
split (rt , a , x , z);
split (x , a - 1 , x , y);
y = merge (tr[y].l , tr[y].r);
rt = merge (merge (x , y) , z);
查询排名为 的数
就是普通的查询,每次把
int kth (int now , int k) {
while (1) {
if (k <= tr[tr[now].l].sz)
now = tr[now].l;
else if (k == tr[tr[now].l].sz + 1)
return now;
else
k -= tr[tr[now].l].sz + 1 , now = tr[now].r;
}
}
查询 的排名 rank
把原树
split (rt , a - 1 , x , y);
printf ("%d\n" , tr[x].sz + 1);
rt = merge (x , y);
查询 的前驱 precursor
找前驱的话把原树按
split (rt , a - 1 , x , y);
printf ("%d\n" , tr[kth (x , tr[x].sz)].v);
rt = merge (x , y);
查询 的后继 successor
跟前驱类似。把原树按
split (rt , a , x , y);
printf ("%d\n" , tr[kth (y , 1)].v);
rt = merge (x , y);
版题
#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
using namespace std;
const int N = 5e5 + 5;
std::mt19937 rnd(233);
int pos , rt;
struct fhq {
int l , r , v , t , sz;
} tr[N];
void update (int x) { tr[x].sz = 1 + tr[tr[x].l].sz + tr[tr[x].r].sz; }
int new_pos (int v) {
tr[++pos].sz = 1;
tr[pos].v = v;
tr[pos].t = rnd ();
return pos;
}
void split (int now , int k , int &x , int &y) {
if (now == 0) x = y = 0;
else {
if (tr[now].v <= k)
x = now , split (tr[now].r , k , tr[now].r , y);
else
y = now , split (tr[now].l , k , x , tr[now].l);
update (now);
}
}
int merge (int x , int y) {
if (x == 0 || y == 0) return x + y;
if (tr[x].t < tr[y].t) {
tr[x].r = merge (tr[x].r , y);
update (x);
return x;
}
else {
tr[y].l = merge (x , tr[y].l);
update (y);
return y;
}
}
int kth (int now , int k) {
while (1) {
if (k <= tr[tr[now].l].sz)
now = tr[now].l;
else if (k == tr[tr[now].l].sz + 1)
return now;
else
k -= tr[tr[now].l].sz + 1 , now = tr[now].r;
}
}
int main () {
int T , op , a , x , y , z;
scanf ("%d" , &T);
while (T --) {
scanf ("%d%d" , &op , &a);
if (op == 1) {
split (rt , a , x , y);
rt = merge (merge (x , new_pos (a)) , y);
}
else if (op == 2) {
split (rt , a , x , z);
split (x , a - 1 , x , y);
y = merge (tr[y].l , tr[y].r);
rt = merge (merge (x , y) , z);
}
else if (op == 3) {
split (rt , a - 1 , x , y);
printf ("%d\n" , tr[x].sz + 1);
rt = merge (x , y);
}
else if (op == 4)
printf ("%d\n" , tr[kth (rt , a)].v);
else if (op == 5) {
split (rt , a - 1 , x , y);
printf ("%d\n" , tr[kth (x , tr[x].sz)].v);
rt = merge (x , y);
}
else {
split (rt , a , x , y);
printf ("%d\n" , tr[kth (y , 1)].v);
rt = merge (x , y);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?