话说天下大事,就像fhq treap —— 分久必合,合久必分


简单讲一讲。非旋treap主要依靠分裂和合并来实现操作。(递归,不维护fa不维护cnt)

合并的前提是两棵树的权值满足一边的最大的比另一边最小的还小。因此时合并时只需要维护键值的堆性质即可。这样每一次比较根节点,如果x比y小那么y直接接到x的右子树即可(需要满足权值的平衡树性质);否则的话只需要反过来,把x接到y的左子树上。merge函数返回的值应当是合并完后的根节点。

分裂分为两种,排名和权值。然而我认为它们本质上是一样的。对于权值的分裂,对于每一个子树的根节点,若根节点比给定值a小,那么此节点及左子树一定比a小,归入x。否则此节点及右子树归入y。然后再递归操作还没有分类的那一个子树就好了。代码实现中的引用用的非常巧妙。可以把这里的引用理解为是需要修改的东西,利用递归的过程对其作出修改。

合并看键值,分裂看权值。


$Merge$

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

$Split$

void split_a(int u, int a, int& x, int& y){
if(!u){ x = y = ; return; }
if(val[u] <= a){
x = u;
split_a(ch[u][], a, ch[u][], y);
}
else{
y = u;
split_a(ch[u][], a, x, ch[u][]);
}
pushup(u);
}
void split_k(int u, int k, int& x, int& y){
if(!u){ x = y = ; return; }
if(size[ch[u][]]+1 <= k){
x = u;
split_k(ch[u][], k-size[ch[u][]]-1, ch[u][], y);
}
else{
y = u;
split_k(ch[u][], k, x, ch[u][]);
}
pushup(u);
}

有了这两个操作,其他操作yy一下即可,非常简便。

当然,在查询k大排名或前驱后继时,完全可以用普通平衡树(如splay)的做法。因为它满足平衡树的性质。也就是傻乎乎的去logn的从根节点往下走。那么既然我们有了split,查询最大最小值应该很方便了。要尽量让代码精简啊!

My Code:

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int MAXN = ;
const int INF = 0x3f3f3f3f;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = ; int w = ; register char c = getchar();
for(; c ^ '-' && (c < '' || c > ''); c = getchar());
if(c == '-') w = -, c = getchar();
for(; c >= '' && c <= ''; c = getchar()) x = (x<<) + (x<<) + c - ''; return x * w;
}
int N,opt,x,num_node,RooT;
int ch[MAXN][],val[MAXN],key[MAXN],size[MAXN];
inline void pushup(int x){
size[x] = size[ch[x][]] + size[ch[x][]] + ;
}
inline void clear(int x){
size[x]=val[x]=key[x]=ch[x][]=ch[x][]=;
}
int merge(int u, int v){
if(!u||!v) return u|v;
if(key[u] < key[v]){
ch[u][] = merge(ch[u][], v);
pushup(u);
return u;
}
else{
ch[v][] = merge(u, ch[v][]);
pushup(v);
return v;
}
}
void split_a(int u, int a, int& x, int& y){
if(!u){ x = y = ; return; }
if(val[u] <= a){
x = u;
split_a(ch[u][], a, ch[u][], y);
}
else{
y = u;
split_a(ch[u][], a, x, ch[u][]);
}
pushup(u);
}
void split_k(int u, int k, int& x, int& y){
if(!u){ x = y = ; return; }
if(size[ch[u][]]+ <= k){
x = u;
split_k(ch[u][], k-size[ch[u][]]-, ch[u][], y);
}
else{
y = u;
split_k(ch[u][], k, x, ch[u][]);
}
pushup(u);
}
inline void Insert(int v){//以v进行分裂,插入后合并。
val[++num_node]= v;
key[num_node] = rand();
size[num_node] = ;
int a,b;
split_a(RooT, v, a, b);
RooT = merge(merge(a,num_node), b);
}
inline void Delete(int v){//以v进行分裂,删除后合并。
int a,b,c,d;
split_a(RooT, v, a, b);
split_k(a, size[a]-, c, d);
clear(d);
RooT = merge(c, b);
}
inline int Rank(int v){//以v-1进行分裂,看左侧树的size
int a,b,ans;
split_a(RooT, v-, a, b);
ans = size[a]+;
RooT = merge(a, b);
return ans;
}
inline int Kth(int k){//以k进行分裂,依旧看左侧的size,但注意再分裂一次取出比k小的
int a,b,c,d,ans;
split_k(RooT, k, a, b);
split_k(a,size[a]-,c,d);
ans = val[d];
RooT = merge(merge(c,d),b);
return ans;
}
inline int Pre(int v){//与kth同理
int a,b,c,d,ans;
split_a(RooT, v-, a, b);
split_k(a,size[a]-,c,d);
ans = val[d];
RooT = merge(merge(c,d),b);
return ans;
}
inline int Nxt(int v){
int a,b,c,d,ans;
split_a(RooT, v, a, b);
split_k(b, , c, d);
ans = val[c];
RooT = merge(a,merge(c,d));
return ans;
}
void PrintTree(int u){
if(ch[u][]) PrintTree(ch[u][]);
printf("%d ",val[u]);
if(ch[u][]) PrintTree(ch[u][]);
}
int main(){
// freopen(".in","r",stdin);
srand((unsigned)time(NULL));
N = read();
while(N--){
opt = read(), x = read();
if(opt == ) Insert(x);
if(opt == ) Delete(x);
if(opt == ) printf("%d\n", Rank(x));
if(opt == ) printf("%d\n", Kth(x));
if(opt == ) printf("%d\n", Pre(x));
if(opt == ) printf("%d\n", Nxt(x));
}
return ;
}

「FHQ Treap」学习笔记的更多相关文章

  1. Fhq Treap [FhqTreap 学习笔记]

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

  2. Note -「Lagrange 插值」学习笔记

    目录 问题引入 思考 Lagrange 插值法 插值过程 代码实现 实际应用 「洛谷 P4781」「模板」拉格朗日插值 「洛谷 P4463」calc 题意简述 数据规模 Solution Step 1 ...

  3. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  4. Note -「单位根反演」学习笔记

    \(\mathcal{Preface}\)   单位根反演,顾名思义就是用单位根变换一类式子的形式.有关单位根的基本概念可见我的这篇博客. \(\mathcal{Formula}\)   单位根反演的 ...

  5. 「Manacher算法」学习笔记

    觉得这篇文章写得特别劲,插图非常便于理解. 目的:求字符串中的最长回文子串. 算法思想 考虑维护一个数组$r[i]$代表回文半径.回文半径的定义为:对于一个以$i$为回文中心的奇数回文子串,设其为闭区 ...

  6. 「线性基」学习笔记and乱口胡总结

    还以为是什么非常高大上的东西花了1h不到就学好了 线性基 线性基可以在\(O(nlogx)\)的时间内计算出\(n\)个数的最大异或和(不需要相邻). 上述中\(x\)表示的最大的数. 如何实现 定义 ...

  7. 「Link-Cut Tree」学习笔记

    Link-Cut Tree,用来解决动态树问题. 宏观上,LCT维护的是森林而非树.因此存在多颗LCT.有点像动态的树剖(链的确定通过$Access$操作),每条链用一颗$splay$维护.$spla ...

  8. 「AC自动机」学习笔记

    AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...

  9. 【Java】「深入理解Java虚拟机」学习笔记(1) - Java语言发展趋势

    0.前言 从这篇随笔开始记录Java虚拟机的内容,以前只是对Java的应用,聚焦的是业务,了解的只是语言层面,现在想深入学习一下. 对JVM的学习肯定不是看一遍书就能掌握的,在今后的学习和实践中如果有 ...

随机推荐

  1. 利用StackExchange.Redis和Log4Net构建日志队列

    简介:本文是一个简单的demo用于展示利用StackExchange.Redis和Log4Net构建日志队列,为高并发日志处理提供一些思路. 0.先下载安装Redis服务,然后再服务列表里启动服务(R ...

  2. 80后程序员降薪6K,预感中年危机来袭,准备跳槽却碰壁

    一提及程序员,很多人想到的都是“工资高”“技术好”诸如此类的,可见程序员是个非常赚钱的职业,所以每年都会有很多毕业生来选择这个行业. 但是社会是公平的,不要只看程序员表面上的光鲜亮丽,其背后也有很多的 ...

  3. Struts2中五个重要的常量

    一.五个常量的位置:位于xwork核心包下的Action字节码文件里 二.五个常量的介绍: a: SUCCESS public static final String SUCCESS = " ...

  4. 前端入门24-响应式布局(BootStrap)

    声明 本篇内容摘抄自以下两个来源: BootStrap中文网 感谢大佬们的分享. 正文-响应式布局(BootStrap) 这次想来讲讲一个前端开发框架:BootStrap BootStrap 目前已经 ...

  5. C#开发命名规范总结整理

    1.  命名规范a) 类[规则1-1]使用Pascal规则命名类名,即首字母要大写.eg:Class Test{    ...}[规则1-2]使用能够反映类功能的名词或名词短语命名类.[规则1-3]不 ...

  6. Android视频录制从不入门到入门系列教程(一)————简介

    一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ...

  7. 【Linux】【MySQL】CentOS7、MySQL8.0.13 骚操作速查笔记——专治各种忘词水土不服

    1.前言 [Linux][MySQL]CentOS7安装最新版MySQL8.0.13(最新版MySQL从安装到运行) 专治各种忘词,各种水土不服. - -,就是一个健忘贵的速查表:(当然不包括SQL的 ...

  8. Nginx设置Https反向代理,指向Docker Gitlab11.3.9 Https服务

    目录 目录 1.GitLab11.3.9的安装 2.域名在阿里云托管,申请免费的1年证书 3.Gitlab 的 https 配置 4.Nginx 配置 https,反向代理指向 Gitlab 配置 目 ...

  9. 太嚣张了!他竟用Python绕过了“验证码”

    在web页面中,经常会遇到验证码,这对于我这么一个热爱web自动化测试人员,就变成了一件头疼的事.于是千方百计找各种资源得到破解简单的验证码方法. 识别验证码 大致分如下几个步骤: 1.获取验证码图片 ...

  10. 斐波那契数列(C#)

    斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:1.1.2.3.5.8.13 ...