众所周知 Fhq Treap 是 fhq 神仙研究出来的平衡树…

具体实现

每个点实现一个 \(\text{rnd}\) 表示 rand 的值 为什么要 rand 呢

是为了保证树高为 \(\log n\) 从而保证复杂度…

FHQ Treap的核心操作是split和merge,其他的操作均以这两个操作为基础进行。

下面所述操作的数据如下所示:

int rt = 0 , cnt = 0 , a[N] , sz[N] , rnd[N] , ch[N][2] ;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]

ls 指的是左儿子 rs 指的是右儿子…

val表示节点的权值 用来维护二叉搜索树的性质,rnk表示节点的权值 用来维护堆的性质。

sz 表示节点大小(包括自身)

split

split 实现的操作大概是 把一棵树分成俩…然后左边的值小于等于 \(k\) 右边的值大于 \(k\)

如果你 split(rt , k , x , y)

那么你就把 rt 分成两个部分 \(x\) ,\(y\) 了 其中 \(x\) 的子树的 \(val \leq k\) , \(y\) 的子树的 \(val > k\)

void split (int cur , int k , int & u , int & v) {
if(! cur) { u = v = 0 ; return ; }
if(a[cur] <= k) { u = cur ; split(rs(u) , k , rs(u) , v) ; }
else { v = cur ; split(ls(v) , k , u , ls(v)) ; }
pushup(cur) ;
}

代码如上 如果小于这个则分给左子树 大于就分给右边……

merge

merge的操作就是合并一下根节点和新建节点 然后保留根/替换根(rnd决定)

反正是随机合并 期望树高 \(\log n\)

int merge (int u , int v) {
if(! u || ! v) return u | v ;
if(rnd[u] < rnd[v]) { rs(u) = merge(rs(u) , v) ; pushup(u) ; return u ; }
else { ls(v) = merge(u , ls(v)) ; pushup(v) ; return v ; }
}

kth

Treap 都满足一个堆的性质… 所以显然…

可以按照 sz 来找 kth 递归/非递归都可以啊…

int kth(int u , int k) {
if(k <= sz[ls(u)]) return kth(ls(u) , k) ;
if(sz[ls(u)] + 1 == k) return a[u] ;
return kth(rs(u) , k - sz[ls(u)] - 1) ;
}

递归写法

rank

rank的话就分离一个 \(val = k-1\) 的子树…然后求这个子树的 sz 就知道 rank 了啊

  int rank(int k) {
int x , y , res ; split(rt , k - 1 , x , y) ;
res = sz[x] + 1 ; rt = merge(x , y) ; return res ;
}

其他操作不讲了

#include <cstdio>
#include <cstdlib>
using ll = long long ;
using namespace std ; int read() {
int x = 0 , f = 1 ; char c = getchar() ;
while(c < '0' || c > '9') { if(c == '-') f = -1 ; c = getchar() ; }
while(c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c & 15) ; c = getchar() ; }
return x * f ;
} template < class T > void print(T x , char c = '\n') {
static char _st[100] ; int _stp = 0 ;
if(x == 0) { putchar('0') ; }
if(x < 0) { putchar('-') ; x = -x ; }
while(x) { _st[++ _stp] = (x % 10) ^ 48 ; x /= 10 ; }
while(_stp) { putchar(_st[_stp --]) ; }
putchar(c) ;
} int q ;
const int N = 1e5 + 10 ;
class Fhq {
public:
int rt = 0 , cnt = 0 , a[N] , sz[N] , rnd[N] , ch[N][2] ;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
int newNode (int x) { sz[++ cnt] = 1 ; a[cnt] = x ; rnd[cnt] = rand() ; return cnt ; }
void pushup (int u) { sz[u] = sz[ls(u)] + sz[rs(u)] + 1 ; }
int merge (int u , int v) {
if(! u || ! v) return u | v ;
if(rnd[u] < rnd[v]) { rs(u) = merge(rs(u) , v) ; pushup(u) ; return u ; }
else { ls(v) = merge(u , ls(v)) ; pushup(v) ; return v ; }
}
void split (int cur , int k , int & u , int & v) {
if(! cur) { u = v = 0 ; return ; }
if(a[cur] <= k) { u = cur ; split(rs(u) , k , rs(u) , v) ; }
else { v = cur ; split(ls(v) , k , u , ls(v)) ; }
pushup(cur) ;
}
void insert(int k) {
int x , y ; split(rt , k , x , y) ; rt = merge(merge(x , newNode(k)) , y) ;
}
void erase(int k) {
int x , y , z ; split(rt , k , x , z) ; split(x , k - 1 , x , y) ;
y = merge(ls(y) , rs(y)) ; rt = merge(merge(x , y) , z) ;
}
int kth(int u , int k) {
if(k <= sz[ls(u)]) return kth(ls(u) , k) ;
if(sz[ls(u)] + 1 == k) return a[u] ;
return kth(rs(u) , k - sz[ls(u)] - 1) ;
}
int rank(int k) {
int x , y , res ; split(rt , k - 1 , x , y) ;
res = sz[x] + 1 ; rt = merge(x , y) ; return res ;
}
int pre(int k) {
int x , y , res ; split(rt , k - 1 , x , y) ;
res = kth(x , sz[x]) ; rt = merge(x , y) ; return res ;
}
int suf(int k) {
int x , y , res ; split(rt , k , x , y) ;
res = kth(y , 1) ; rt = merge(x , y) ; return res ;
}
} T ;
int main() {
srand(19260817) ;
q = read() ;
while(q --) {
int opt = read() , x = read() ;
if(opt == 1) { T.insert(x) ; }
if(opt == 2) { T.erase(x) ; }
if(opt == 3) { print(T.rank(x)) ; }
if(opt == 4) { print(T.kth(T.rt , x)) ; }
if(opt == 5) { print(T.pre(x)) ; }
if(opt == 6) { print(T.suf(x)) ; }
}
return 0 ;
}

同样的 fhq 不仅仅是可以用来按 \(val\) 分离 还可以用 \(size\) 分离成两棵树 (文艺平衡树代码)

#include <cstdio>
#include <cstdlib>
using ll = long long ;
using namespace std ; int read() {
int x = 0 , f = 1 ; char c = getchar() ;
while(c < '0' || c > '9') { if(c == '-') f = -1 ; c = getchar() ; }
while(c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + (c & 15) ; c = getchar() ; }
return x * f ;
} template < class T > void print(T x , char c = '\n') {
static char _st[100] ; int _stp = 0 ;
if(x == 0) { putchar('0') ; }
if(x < 0) { putchar('-') ; x = -x ; }
while(x) { _st[++ _stp] = (x % 10) ^ 48 ; x /= 10 ; }
while(_stp) { putchar(_st[_stp --]) ; }
putchar(c) ;
} int n , q ;
const int N = 1e5 + 10 ;
class Fhq {
public:
int ch[N][2] , rnd[N] , a[N] , sz[N] , rt = 0 , cnt = 0 ;
bool rev[N] ;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
void pushup(int u) { sz[u] = sz[ls(u)] + sz[rs(u)] + 1 ; }
void swap(int & x , int & y) { x ^= y ^= x ^= y ; }
void pushr(int u) { swap(ls(u) , rs(u)) ; rev[u] ^= 1 ; }
void pushdown(int u) {
if(! rev[u]) return ;
if(ls(u)) pushr(ls(u)) ;
if(rs(u)) pushr(rs(u)) ;
rev[u] ^= 1 ;
}
int merge(int x , int y) {
if(! x || ! y) return x | y ;
if(rnd[x] < rnd[y]) {
pushdown(x) ;
rs(x) = merge(rs(x) , y) ;
pushup(x) ;
return x ;
}
pushdown(y) ;
ls(y) = merge(x , ls(y)) ;
pushup(y) ;
return y ;
}
void split(int cur , int k , int & u , int & v) {
if(! cur) { u = v = 0 ; return ; }
pushdown(cur) ;
if(sz[ls(cur)] < k) {
u = cur ;
split(rs(u) , k - sz[ls(cur)] - 1 ,rs(u) , v) ;
}
else {
v = cur ;
split(ls(v) , k , u , ls(v)) ;
}
pushup(cur) ;
}
void reverse(int l , int r) {
int x , y , z ; x = y = z = 0 ;
split(rt , l - 1 , x , y) ;
split(y , r - l + 1 , y , z) ;
pushr(y) ;
rt = merge(merge(x , y) , z) ;
}
int newNode(int x) { sz[++ cnt] = 1 ; a[cnt] = x ; rnd[cnt] = rand() ; return cnt ; }
void push_back(int x) { rt = merge(rt , newNode(x)) ; }
void dfs(int u) {
pushdown(u) ;
if(ls(u)) dfs(ls(u)) ;
print(a[u] , ' ') ;
if(rs(u)) dfs(rs(u)) ;
}
} T ;
int main() {
srand(19260817) ;
n = read() ; q = read() ;
for(int i = 1 ; i <= n ; i ++) T.push_back(i) ;
while(q --) {
int l = read() , r = read() ;
T.reverse(l , r) ;
}
T.dfs(T.rt) ;
return 0 ;
}

自行理解 我顺便解释一下子 fhq是一种按中序遍历建树的玩意 所以如果需要输出就直接按中序遍历输出了 ovo

如果有没看懂的欢迎评论 quq

Fhq Treap [FhqTreap 学习笔记]的更多相关文章

  1. 「FHQ Treap」学习笔记

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

  2. [普通平衡树treap]【学习笔记】

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 9046  Solved: 3840[Submit][Sta ...

  3. 可持久化fhq-treap学习笔记

    目录 可持久化fhq-treap----- 支持查询历史版本的非旋treap 先看看为啥他可以可持久化 过程 别的 注意&&出错&&吐槽 模板->luoguP38 ...

  4. FHQ-Treap学习笔记

    平衡树与FHQ-Treap 平衡树(即平衡二叉搜索树),是通过一系列玄学操作让二叉搜索树(BST)处于较平衡的状态,防止在某些数据下退化(BST在插入值单调时,树形不平衡,单次会退化成 \(\math ...

  5. 树堆(Treap)学习笔记 2020.8.12

    如果一棵二叉排序树的节点插入的顺序是随机的,那么这样建立的二叉排序树在大多数情况下是平衡的,可以证明,其高度期望值为 \(O( \log_2 n )\).即使存在一些极端情况,但是这种情况发生的概率很 ...

  6. fhq treap 学习笔记

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

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

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

  8. 「学习笔记」 FHQ Treap

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

  9. fhq treap抄袭笔记

    目录 碎碎念 点一下 注意!!! 模板 fhq treap 碎碎念 我咋感觉合并这么像左偏树呢 ps:难道你们的treap都是小头堆的吗 fhq真的是神人 现在看以前学的splay是有点恶心,尤其是压 ...

随机推荐

  1. Dubbo(二):深入理解Dubbo的服务发现SPI机制

    一.前言 用到微服务就不得不来谈谈服务发现的话题.通俗的来说,就是在提供服务方把服务注册到注册中心,并且告诉服务消费方现在已经存在了这个服务.那么里面的细节到底是怎么通过代码实现的呢,现在我们来看看D ...

  2. 三、Linux系统中的文件类型和文件扩展名

    .sock文件也是一类特殊的文件,这类文件通常用在网络之间进行数据连接,如:我们可以启动一个程序来监听客户端的要求,客户端可以通过套接字来进行通信: linux中的文件类型 文件类型介绍 Linux系 ...

  3. MCI使用

    MCI(Media Control Interface)媒体控制接口是MircroSoft提供的一组多媒体设备和文件的标准接口,它的好处是可以方便地控制绝大多数多媒体设备包括音频.视频.影碟.录像等多 ...

  4. docker nginx 实现图片预览

    一.实现 nginx http图片预览 1.创建本地配置文件目录以及配置文件 两种方式: 1.1.docker nginx将配置文件抽离到了/etc/nginx/conf.d,只需要配置default ...

  5. ccf

    import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; public class MST { pu ...

  6. 从数组中找到topK的元素(序号)

    问题: 在n个数中找出最大的k个数. 多次求min()或求max() 最简单的方法是对大小为k的数组进行n次求min计算(或者对大小为n的数组进行k次求max计算)最后能够找出最大k个数.复杂度是O( ...

  7. 第一次安装android studio遇到的问题

    安装android studio一点都不顺利,最后总是成功安装,但是忘了把问题记录下来,下一次肯定还肯能出现问题 忘了把问题记录下来了,好像是sync failed 第一次安装3.1.4遇到的问题,好 ...

  8. 《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第一节:画直线

    今天正式开一本新书,<C# GDI+ 破镜之道>,同样是破镜之道系列丛书的一分子. 关于GDI+呢,官方的解释是这样的: GDI+ 是 Microsoft Windows 操作系统的窗体子 ...

  9. 详解c++中对二维数组下标[][]的重载

    首先定义一个矩阵类,我用一个二维数组存储矩阵中的数据,矩阵详细定义如下 class Matrix { public: Matrix(int rows, int cols) { _rows = rows ...

  10. Linux 工作机初始化操作

    # apt 依赖包安装 sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libs ...