fhq_treap

这东西据说是某个叫范浩强的神仙搞出来的,

他的这种treap可以不用旋转并且资磁很多平衡树操作,

复杂度通过随机的键值来保证(树大致平衡,期望一次操作复杂度\(logn\))

依靠核心函数split和merge实现绝大多数操作

首先建树的话可以笛卡尔树优化到\(O(n)\),暴力merge\(O(nlogn)\)

通过以下几个操作进行说明(以下默认权值与v相同split到左边)

  • 插入数v:将原树从v的位置分裂成x,y,合并x,v,再合并x,y.
  • 删除数v:将原树从v-1分裂成x,y,将y从v分成y,z,那么将y树的根删去(\(merge(ls_y,rs_y)\))

    (ps:如果相同权值的全删掉,那么整个y树都不要了),接着把剩下x,y,z的merge回来.
  • 查询v的rank(rank定义为比v小的数的个数+1):那么从v-1处split成x,y,返回x树的sz+1.
  • 查询rank为k的数:同普通treap,不详述.
  • 查询v的前驱:从v-1处split成x,y,返回x树的最后一个,为空则无.(ps:查询第sz个用求k大的方法)
  • 查询v的前驱:从v处split成x,y,返回y树的第一个,为空则无.

split

上述操作的split均按权值分裂,我们先来看看split函数

[按权值split]

void split(int x,int&l,int&r,int k){
if(!x){l=r=0;return;}//到空节点返回0
if(val[x]<=k){l=x;split(rs[l],rs[l],r,k);pu(l);}//x分给左树,接着分x的右儿子
else{r=x;split(ls[r],l,ls[r],k);pu(r);}//x分给右树,接着分x的左儿子
}

[按下标split]

void split(int x,int&l,int&r,int k){
if(!x){l=r=0;return;}
if(sz[ls[x]]+1<=k){l=x;split(rs[l],rs[l],r,k-sz[ls[x]]-1);pu(l);}//注意修改k
else{r=x;split(ls[r],l,ls[r],k);pu(r);}
}

对于我们遍历到每一个点,假如它的权值小于等于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子.

假如大于k,把它的所有右子树分到右边的树里,遍历左儿子.

merge

再看merge函数(默认满足大根堆性质)

void merge(int&x,int l,int r){
if(!l||!r){x=l|r;return;}//l或r为空则返回另一个
if(fix[l]>fix[r]){x=l;merge(rs[x],rs[x],r);}//按键值确定父子关系
else{x=r;merge(ls[x],l,ls[x]);}pu(x);
}

由于第一棵树的权值都小于第二棵树,那么就只需要比较键值确定父子关系

如果fix[l]>fix[r],那么比较l的右儿子和r

否则比较r的左儿子和l,递归merge

以上是一些基本操作,有了这些可以完成[模板]普通平衡树

放几个函数的code

void insert(int v){
int x,y;split(rt,x,y,v-1);
merge(x,x,newnode(v));merge(rt,x,y);
}
void del(int v){
int x,y,z;split(rt,x,y,v);split(x,x,z,v-1);
merge(z,ls[z],rs[z]);merge(x,x,z);merge(rt,x,y);
}
void rk(int v){
int x,y;split(rt,x,y,v-1);
printf("%d\n",sz[x]+1);merge(rt,x,y);
}
int kth(int k){
int x=rt;
while(1){
if(k==sz[ls[x]]+1)return val[x];
if(k<=sz[ls[x]])x=ls[x];
else k-=sz[ls[x]]+1,x=rs[x];//注意先修改k!!!
}
}
void pre(int v){
int x,y;split(rt,x,y,v-1);
printf("%d\n",sz[x]?kth(x,sz[x]):-inf);
merge(rt,x,y);
}
void suf(int v){
int x,y;split(rt,x,y,v);
printf("%d\n",sz[y]?kth(y,1):inf);
merge(rt,x,y);
}

有的时候我们需要对一个区间进行操作,这时只要

void XXX(int l,int r){
int x,y,z;split(rt,x,y,r);split(x,x,z,l-1);
//do sth such as put reverse tag,put add tag
merge(x,x,z);merge(rt,x,y);
}

可持久化

说起来目前为止这都是很多平衡树都能维护的东西,

而且我们发现每次操作都需要split和merge多次,这会是一个较大的常数

那么它牛逼在哪里?

它容易写

它可以持久化.

我们只需要将原先的根rt开成rt[],每次访问v版本就在rt[v]上查,

我们知道可持久化对修改有一个要求就是修改不能影响之前的版本,也就是不能改变先前版本的树的形态

我们发现涉及形态修改的函数只有merge和split,于是我们稍作修改

[可持久化split]

void split(int x,int&l,int&r,int k){
if(!x){l=r=0;return;}
if(k>=val[x]){l=++tot;cp(l,x);split(rs[l],rs[l],r,k);pu(l);}//cp即copy,把节点复制过来
else{r=++tot;cp(r,x);split(ls[r],l,ls[r],k);pu(r);}
}

[可持久化merge]

void merge(int&x,int l,int r){
if(!l||!r){x=l|r;return;}x=++tot;//新建节点
if(fix[l]>fix[r]){cp(x,l);merge(rs[x],rs[x],r);}
else{cp(x,r);merge(ls[x],l,ls[x]);}pu(x);
}

讨论中有提到merge不用再开点,因为点已经在split中建好了

对此博主并不太清楚,欢迎大佬指教

我们发现空间是\(nlogn\)(n是修改操作数)的,由于一次操作需要多次split,merge所以空间还要乘个常数

有的带删除操作的题我们可以考虑垃圾车回收节点节省空间

有了这些我们可以完成[模板]可持久化平衡树

其他平衡树的题应该都可以写了嗯

end

[note]fhq_treap的更多相关文章

  1. 三星Note 7停产,原来是吃了流程的亏

    三星Note 7发售两个月即成为全球噩梦,从首炸到传言停产仅仅47天.所谓"屋漏偏逢连天雨",相比华为.小米等品牌对其全球市场的挤压.侵蚀,Galaxy Note 7爆炸事件这场连 ...

  2. 《Note --- Unreal --- MemPro (CONTINUE... ...)》

    Mem pro 是一个主要集成内存泄露检测的工具,其具有自身的源码和GUI,在GUI中利用"Launch" button进行加载自己待检测的application,目前支持的平台为 ...

  3. 《Note --- Unreal 4 --- Sample analyze --- StrategyGame(continue...)》

    ---------------------------------------------------------------------------------------------------- ...

  4. [LeetCode] Ransom Note 赎金条

    
Given
 an 
arbitrary
 ransom
 note
 string 
and 
another 
string 
containing 
letters from
 all 
th ...

  5. Beginning Scala study note(9) Scala and Java Interoperability

    1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...

  6. Beginning Scala study note(8) Scala Type System

    1. Unified Type System Scala has a unified type system, enclosed by the type Any at the top of the h ...

  7. Beginning Scala study note(7) Trait

    A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...

  8. Beginning Scala study note(6) Scala Collections

    Scala's object-oriented collections support mutable and immutable type hierarchies. Also support fun ...

  9. Beginning Scala study note(5) Pattern Matching

    The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...

随机推荐

  1. 2017.4.26 慕课网--Java 高并发秒杀API(一)

    Java高并发秒杀API系列(一) -----------------业务分析及Dao层 第一章 课程介绍 1.1 内容介绍及业务分析 (1)课程内容 SSM框架的整合使用 秒杀类系统需求理解和实现 ...

  2. C++ 设置文件最近修改时间

    利用VS开发C++项目,经常发现修改系统时间后,每次编译过程会变得很慢,其原因就是当你把系统时间调到未来的一个时间点,然后有意或者无意编辑过一些代码文件,那么这些文件的时间戳就停留在未来. 当你把系统 ...

  3. 创建你的第一个ionic+cordova应用(1)

    前面我们安装了前端的神器webstorm11,体验到了强大的开发体验,接着我们来安装ionic 必备: Node.js (npm安装工具) 百度下载 官网下载  注:如果官网新版不能安装请用百度下载0 ...

  4. UVA 10679 I love Strings!!!(AC自己主动机)

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...

  5. 通用对话弹窗CommonDialog

    代码地址如下:http://www.demodashi.com/demo/12592.html 通用对话弹窗CommonDialog Version 1.0 Created by chenchangj ...

  6. SQL中拆分字符串substr及统计字符出现频数replace用法实例讲解

    一.拆分字符串为若干行 例一:要求将表emp中的'king'按照每行一个单词拆成四行 注意:substr(str,pos):截取pos位置开始的字符: substr(str,pos,len):从pos ...

  7. java 乱码问题解决思路

    "编码一致的条件下,在处理运行正常的情况下,是不会出现乱码的",记住这句金言. 如上所说,如果编码一致是不会出现这种乱码问题,所以解决办法就是仔细再仔细的检查所设置的编码是否是一致 ...

  8. ASP.NET常见命名空间及其功能描述

    命名空间 |  功能描述 System |  包含CLR的基本类型和基类,定义了常用的值类型和引用类型,事件.接口.属性和异常处理等 System.Text | 包含用于文本处理的类,实现了不同编码方 ...

  9. <交流贴>android语音识别之科大讯飞语音API的使用

      因为最近在研究语音识别,所以借鉴了一下CreAmazing网友的帖子 Android系统本身其实提供有语音识别模块,在它的APIDemo里也有关于语音识别的sample,不过经过大多开发者的真机测 ...

  10. css 温故而知新 select-option 文字方向居右

    对select-option使用text-align:right;是无效的. 正确的姿势是:direction: ltr; 另外值得一提的是,通常还需要配合一点padding来美化.