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. [转] docker rmi命令-删除image

    原文:http://www.simapple.com/341.html ---------------------------------------------------------------- ...

  2. 关于批量插入数据之我见(100万级别的数据,mysql)

    因前段时间去面试,问到怎样高效向数据库插入10万条记录,之前没处理过类似问题.也没看过相关资料,结果没答上来,今天就查了些资料.总结出三种方法: 測试数据库为mysql!!! 方法一: public ...

  3. Elasticsearch 基础使用

    使用 cURL 执行 REST 命令 可以对 Elasticsearch 发出 cURL 请求,这样很容易从命令行 shell 体验该框架. “Elasticsearch 是无模式的.它可以接受您提供 ...

  4. 13.1Springboot 之 静态资源路径配置

    Spring 静态资源路径是指系统可以直接访问的路径,且路径下的所有文件均可被用户直接读取. 在Springboot中默认的静态资源路径有:classpath:/META-INF/resources/ ...

  5. vue笔记四

    十一.过渡与动画 1.使用限制Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加 entering/leaving 过渡条件渲染 (使用 v-if)条件展示 (使 ...

  6. redis基本数据类型及命令

    一.key相关命令 redis本质上是key-value数据库.所以key操作适用于redis的任何数据类型缓存. 1.exists key判断是否存在key,存在返回1,不存在返回0 2.del k ...

  7. scrapy 安装详解

    一. Scrapy简介 Scrapy is a fast high-level screen scraping and web crawling framework, used to crawl we ...

  8. 【原创】Loadrunner使用json格式请求数据并参数化

    (2015-04-10 16:10:41) 转载▼ 标签: loadrunner json 参数化 web_custom_request 分类: 性能测试 请求自定义的http文件用函数:web_cu ...

  9. 实现Tab功能

    网上实现Tab功能的方法有很多,这里我使用Fragment的方法,我觉着比较简单易懂 MainActivity private android.app.FragmentManager fragment ...

  10. IOS 10 微信 ajax readystate=0 status=0 解决方法

    最近做了一个 基于微信访问的网页系统 发现IOS10.2.1 版本 访问的时候 AJAX报错,安卓和IOS11.4.1 没有这样的问题. 通过Fiddler抓包发现,AJAX请求时 报错信息为  {& ...