「FHQ Treap」学习笔记
话说天下大事,就像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」学习笔记的更多相关文章
- Fhq Treap [FhqTreap 学习笔记]
众所周知 Fhq Treap 是 fhq 神仙研究出来的平衡树- 具体实现 每个点实现一个 \(\text{rnd}\) 表示 rand 的值 为什么要 rand 呢 是为了保证树高为 \(\log ...
- Note -「Lagrange 插值」学习笔记
目录 问题引入 思考 Lagrange 插值法 插值过程 代码实现 实际应用 「洛谷 P4781」「模板」拉格朗日插值 「洛谷 P4463」calc 题意简述 数据规模 Solution Step 1 ...
- Note -「动态 DP」学习笔记
目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...
- Note -「单位根反演」学习笔记
\(\mathcal{Preface}\) 单位根反演,顾名思义就是用单位根变换一类式子的形式.有关单位根的基本概念可见我的这篇博客. \(\mathcal{Formula}\) 单位根反演的 ...
- 「Manacher算法」学习笔记
觉得这篇文章写得特别劲,插图非常便于理解. 目的:求字符串中的最长回文子串. 算法思想 考虑维护一个数组$r[i]$代表回文半径.回文半径的定义为:对于一个以$i$为回文中心的奇数回文子串,设其为闭区 ...
- 「线性基」学习笔记and乱口胡总结
还以为是什么非常高大上的东西花了1h不到就学好了 线性基 线性基可以在\(O(nlogx)\)的时间内计算出\(n\)个数的最大异或和(不需要相邻). 上述中\(x\)表示的最大的数. 如何实现 定义 ...
- 「Link-Cut Tree」学习笔记
Link-Cut Tree,用来解决动态树问题. 宏观上,LCT维护的是森林而非树.因此存在多颗LCT.有点像动态的树剖(链的确定通过$Access$操作),每条链用一颗$splay$维护.$spla ...
- 「AC自动机」学习笔记
AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...
- 【Java】「深入理解Java虚拟机」学习笔记(1) - Java语言发展趋势
0.前言 从这篇随笔开始记录Java虚拟机的内容,以前只是对Java的应用,聚焦的是业务,了解的只是语言层面,现在想深入学习一下. 对JVM的学习肯定不是看一遍书就能掌握的,在今后的学习和实践中如果有 ...
随机推荐
- Flutter 即学即用系列博客——03 在旧有项目引入 Flutter
前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...
- ado.net的简单数据库操作(二)之封装SqlHelperl类
今天我书接上回,接着昨天的ado.net的数据库操作的相关知识来讲哈! 从上篇文章给出的实例来看,你一定会发现,操作数据库其实还挺麻烦的,就连一个最简单的数据库操作语句都要包括 定义数据库连接字符串. ...
- 【Oracle学习笔记】定时任务(dbms_job)
一.概述 Oralce中的任务有2种:Job和Dbms_job,两者的区别有: 1. jobs是oracle数据库的对象, dbms_jobs只是jobs对象的一个实例, 就像对于tables, e ...
- 从零开始学安全(四十一)●初识Wireshark
wireshark:Beyond Compare是一个网络封包分析软件.网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与 ...
- nginx系列12:一致性哈希算法
前面一节的hash算法存在一个问题,当上游的应用服务器因某一台down掉导致服务器数量发生变化时,会导致大量的请求路由策略失效,一致性哈希算法可以缓解这个问题. 一致性哈希算法 1,hash算法存在的 ...
- 洛谷P2664 树上游戏(点分治)
题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...
- Your project path contains non-ASCII characters
Android studio导入project时报错 non-ASCII characters意味着中文字符报错,解决方法简单有效: 检查项目路径中是否出现中文名,将中文字符修改成英文就可以解决辽~
- Python 强制停止多线程运行
强制停止多线程运行 by:授客 QQ:1033553122 #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'shouke' ...
- Parcelable 小记
Parcelable 类,接口类,用于数据的序列化封装.常见的Bundle,Intent类都实现了该类. 实现该类需要实现writeToParcel和describeContents方法,最后还需 ...
- Java数据解析之XML
文章大纲 一.XML解析介绍二.Java中XML解析介绍三.XML解析实战四.项目源码下载 一.XML解析介绍 最基础的XML解析方式有DOM和SAX,DOM和SAX是与平台无关的官方解析方式 ...