[note]fhq_treap
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的更多相关文章
- 三星Note 7停产,原来是吃了流程的亏
三星Note 7发售两个月即成为全球噩梦,从首炸到传言停产仅仅47天.所谓"屋漏偏逢连天雨",相比华为.小米等品牌对其全球市场的挤压.侵蚀,Galaxy Note 7爆炸事件这场连 ...
- 《Note --- Unreal --- MemPro (CONTINUE... ...)》
Mem pro 是一个主要集成内存泄露检测的工具,其具有自身的源码和GUI,在GUI中利用"Launch" button进行加载自己待检测的application,目前支持的平台为 ...
- 《Note --- Unreal 4 --- Sample analyze --- StrategyGame(continue...)》
---------------------------------------------------------------------------------------------------- ...
- [LeetCode] Ransom Note 赎金条
Given an arbitrary ransom note string and another string containing letters from all th ...
- 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 ...
- 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 ...
- Beginning Scala study note(7) Trait
A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...
- Beginning Scala study note(6) Scala Collections
Scala's object-oriented collections support mutable and immutable type hierarchies. Also support fun ...
- Beginning Scala study note(5) Pattern Matching
The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...
随机推荐
- android开发常用地址
一. android市场占用率的url http://developer.android.com/about/dashboards/ 二. ADT下载 下载地址是:http://developer.a ...
- agruments应用——求出函数参数的总合&&css函数——设置/读取对象的属性&&当前输入框高亮显
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Solidworks如何在装配图中保存单独的一个零件
如下图所示,我想要保存装配体的一个单独的零部件 选中该零件后点击编辑零部件 然后点击顶部的文件-另存为,弹出"解决模糊情形"对话框,询问你要保存装配体还是零部件 点击确 ...
- python中MySQLdb模块用法实例
篇文章主要介绍了python中MySQLdb模块用法,以实例形式详细讲述了MySQLdb模块针对MySQL数据库的各种常见操作方法,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了python中 ...
- C++ 利用文件流复制文件
bool CopyFile(const std::string &src, const std::string &dest) { std::ifstream fin(src.c_str ...
- 快速构建大数据存储分析平台-ELK平台安装
一.概述 ELK是由Elastic公司开发的Elasticsearch.Logstash.Kibana三款开源软件的缩写(但不限于这三款软件). 为什么使用ELK? 在目前流行的微服务架构中,一个大型 ...
- OS之os.fork()
有两种方式来实现并发性, 一种方式是让每个“任务"或“进程”在单独的内在空间中工作,每个都有自已的工作内存区域.不过,虽然进程可在单独的内存空间中执行,但除非这些进程在单独的处理器上执行,否 ...
- JavaScript实现深拷贝(深复制) 面试题
1.两种方法实现深拷贝(深复制) (1)方法一:兼容性好,请仔细看代码(网上大部分代码有Bug) (2)方法二:需要对象满足JSON数据格式.JOSN数据格式:http://www.cnblogs.c ...
- 微软认知服务:QnA Maker使用示例
简介 QnA Maker 从用户提供的内容(包括 FAQ URL.文档和编辑内容)中提取所有可能存在的问答对:利用易于使用的界面编辑.删除和添加问答对,然后将知识库作为 API 终结点进行发布:QnA ...
- Android 完整开源应用大全,完整开源项目
(Antox)聊天的 (new) (OpenKeychain)OpenPGP在android上的实现 (new) (Flock)提供同步服务 (OpenFlappyBird)以前火爆的坑爹鸟 (F ...