[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 ...
随机推荐
- [转] docker rmi命令-删除image
原文:http://www.simapple.com/341.html ---------------------------------------------------------------- ...
- 关于批量插入数据之我见(100万级别的数据,mysql)
因前段时间去面试,问到怎样高效向数据库插入10万条记录,之前没处理过类似问题.也没看过相关资料,结果没答上来,今天就查了些资料.总结出三种方法: 測试数据库为mysql!!! 方法一: public ...
- Elasticsearch 基础使用
使用 cURL 执行 REST 命令 可以对 Elasticsearch 发出 cURL 请求,这样很容易从命令行 shell 体验该框架. “Elasticsearch 是无模式的.它可以接受您提供 ...
- 13.1Springboot 之 静态资源路径配置
Spring 静态资源路径是指系统可以直接访问的路径,且路径下的所有文件均可被用户直接读取. 在Springboot中默认的静态资源路径有:classpath:/META-INF/resources/ ...
- vue笔记四
十一.过渡与动画 1.使用限制Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加 entering/leaving 过渡条件渲染 (使用 v-if)条件展示 (使 ...
- redis基本数据类型及命令
一.key相关命令 redis本质上是key-value数据库.所以key操作适用于redis的任何数据类型缓存. 1.exists key判断是否存在key,存在返回1,不存在返回0 2.del k ...
- scrapy 安装详解
一. Scrapy简介 Scrapy is a fast high-level screen scraping and web crawling framework, used to crawl we ...
- 【原创】Loadrunner使用json格式请求数据并参数化
(2015-04-10 16:10:41) 转载▼ 标签: loadrunner json 参数化 web_custom_request 分类: 性能测试 请求自定义的http文件用函数:web_cu ...
- 实现Tab功能
网上实现Tab功能的方法有很多,这里我使用Fragment的方法,我觉着比较简单易懂 MainActivity private android.app.FragmentManager fragment ...
- IOS 10 微信 ajax readystate=0 status=0 解决方法
最近做了一个 基于微信访问的网页系统 发现IOS10.2.1 版本 访问的时候 AJAX报错,安卓和IOS11.4.1 没有这样的问题. 通过Fiddler抓包发现,AJAX请求时 报错信息为 {& ...