话说天下大事,就像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. Asp.net MVC 中 CodeFirst 开发模式实例

    昨天写的这篇博客因为下班时间到了忘记保存了,好郁闷,得重新写一遍.实习所在公司使用的是CodeFirst开发模式,最近开始参与到公司的项目里面来了,发现这个模式特别好用,建库建表改变字段属性添加删除字 ...

  2. C# 曲线上的点(一) 获取指定横坐标对应的纵坐标值

    获取直线上的点,很容易,那曲线呢?二阶贝塞尔.三阶贝塞尔.多段混合曲线,如何获取指定横坐标对应的纵坐标? 如下图形: 实现方案 曲线上的点集 Geometry提供了一个函数GetFlattenedPa ...

  3. java_方法引用

    什么是方法引用? 个人简述方法引用: 方法引用主要是针对已经有的方法来让目前的编程更加简洁 当我们想要使用一个接口的子类的时候,子类需要重写这个接口中的抽象方法, 被重写的这个方法参数列表固定,返回值 ...

  4. PHP+nginx 启动后访问超时

    场景 在Windows上, nginx配置并启动后, 访问报504超时 解决 很尴尬, php-cgi没启动 php-cgi -b

  5. 15个常用的javaScript正则表达式

    1 用户名正则 //用户名正则,4到16位(字母,数字,下划线,减号) ,}$/; //输出 true console.log(uPattern.test("iFat3")); 2 ...

  6. C# E店宝格格家接口对接

    一.实现图片 二.实现通用方法 /// <summary> /// 调用通用方法 /// </summary> /// <param name="strURL& ...

  7. vue环境搭建及项目介绍

    搭建开发环境(搭建开发环境前必须安装node.js): 1.安装vue脚手架工具 $ npm install -g vue-cli 2.创建项目(注意项目名字不要有大写字母) vue init < ...

  8. EF6实现软删除

    https://www.jianshu.com/p/c65fbfe16e1a

  9. python接口自动化-json数据处理

    前言 有些post的请求参数是json格式的,需要导入json模块进行处理,json是一种数据交换格式,独立于编程语言 一般常见的接口返回数据也是json格式的,我们在做判断的时候,往往只需要提取其中 ...

  10. 机器学习:利用K-均值聚类算法对未标注数据分组——笔记

    聚类: 聚类是一种无监督的学习,它将相似的对象归到同一个簇中.有点像全自动分类.聚类方法几乎可以应用于所有对象,簇内的对象越相似,聚类的效果越好.聚类分析试图将相似对象归入同一簇,将不相似对象归到不同 ...