【数据结构】【平衡树】无旋转treap
最近在研究平衡树,看起来这种东西又丧水又很深,感觉很难搞清楚。在Ditoly学长的建议下,我先学习了正常的treap,个人感觉这应该是平衡树当中比较好懂的而且比较好写的一种。
然而,发现带旋treap有很多无法支持的操作,例如各种区间操作,而且由于会旋转无法可持久化,这是一个十分影响实用性的问题,在没有办法支持区间操作的情况下,我有2种选择:
1)滚去学splay;2)学习无旋treap 正常人应该都会去学习splay,然而我选择了后者,因为貌似splay在FJ省选R2T1中被卡成傻逼了。。。。有个学长因此此题爆0。。。心疼。。。。。
所以准备先搞一搞无旋treap,再去搞splay。
**********************华丽的分割线***************************************************
以下是正文。
无旋treap,顾名思义就是没有了rotate的treap,而我们在无旋treap中十分重要的就是2个基础操作,可以说这2个操作才给我们带来了使用无旋treap的理由,它们就是merge(合并treap)和split(分离treap)。
你Insert要用到它们,Delete要用到它们,查询还是需要它们。。。。
这里首先强调一点,merge中合并的2棵treap是要求相对有序的,换而言之就是其中一棵treap的最右节点要严格不大于另一棵的最左节点,不然就和永无乡那题一样,你得写启发式合并暴力merge了,有兴趣的可以看一下那一篇的题解,然而和本题没有并没有毛线关系23333。
然后先看一道例题吧。。。这题在之前我写了一个标准的普通版treap,题解戳这里,不了解普通treap的和没看过原题的可以先看这一篇(当然还是建议在彻底理解的前提下阅读本文,而且本人语文感人,表达能力可能不佳,见谅,如果想要深入学习,建议看dalao的这篇)。
首先这是一道平衡树模板题,因为没有区间操作,所以你写普通treap是能过的。
接下来带有区间操作的模板题我另开一篇写了一道模板题,传送门戳我。
然后讲一下基本操作的实现:(不懂的可以最后结合本人代码理解:D)
I.merge(A,B)
A和B是2棵treap,实际上我们操作上是2个root。
首先我们默认max(A)是小于min(B)的。
然后当前的2棵treap的root节点,我们判断一下优先级,然后默认小的扔上面(维护treap性质),例如:
若A.pri<B.pri(即合并后A为B的祖先),我们显然A的左子树不需更改,而我们需要更改A的是右子树,因此令A的右儿子为merge(A.rightson,B)的root即可。
反之同理即上述内容的反演。
II.split(A,k)
split操作是分离A这棵treap中前K大的和后面的。
显然你只需要按照rank跑一遍找到分界点,然后根据每次是向左跑还是向右跑决定这个点属于哪一棵分出来的树:
1)向左跑说明这个点的权值较大,它以及它的右子树均属于第二棵treap;2)向右跑说明这个点及其左子树均被在前K个,属于第一棵treap。
因为这样的treap,没有了旋转操作,因此是可持久化的,这里改日再讲。
接下来回归模板题的基本操作。首先先默认权值相等的我们扔到右子树。
I)Insert 插入一个权值为val的节点。
我们只需要查找这个点在treap中的rank(记为k),然后split之后增加这个节点进行merge即可。
II)Delete 删去一个权值为val的节点。
我们只需要查找这个节点在treap中的rank,然后split分离出这个节点,然后对于剩下的2棵treap进行merge即可。
III)getkth 查找权值val在树上的最小排名。
由于我们正常查找返回的是最后的位置,所以查找(val-1)在树上的rank再+1即可。
IV)findkth 查找树上排名为rank的权值。
直接split分离这个点然后记录权值后再merge回去。
V)prefix 查找树上权值小于val的最大值。
直接findkth(getkth(val-1))即可。
VI)suffix查找树上权值大于val的最小值。
直接findkth(getkth(val)+1)即可。
接下来给出代码帮助大家理解,当然是跑得没有普通treap快的就是了。
#include <stdio.h>
#include <algorithm>
#define r register
#define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
char BB[<<],*S=BB,*TT=BB;
inline int in(){
r int x=;r bool f=;r char c;
for (;(c=getchar())<''||c>'';) f=c=='-';
for (x=c-'';(c=getchar())>=''&&c<='';) x=(x<<)+(x<<)+c-'';
return f?-x:x;
}
namespace Treap{
inline int Rand(){
static int x=;
return x^=x<<,x^=x>>,x^=x<<;
}
struct node{
node *ls,*rs;
int val,pri,sz;
node(int val):val(val),pri(Rand()),ls(NULL),rs(NULL),sz(){};
inline void combine(){sz=+(ls?ls->sz:)+(rs?rs->sz:);}
}*root;
inline int Size(node *x){return x?x->sz:;}
node *merge(node *a,node *b){
if (!a) return b;if (!b) return a;
if (a->pri<b->pri){
a->rs=merge(a->rs,b);
a->combine();
return a;
}else{
b->ls=merge(a,b->ls);
b->combine();
return b;
}
}
typedef std::pair<node*,node*> Droot;
Droot split(node *x,int k){
if (!x) return Droot(NULL,NULL);
r Droot y;
if (Size(x->ls)>=k){
y=split(x->ls,k);
x->ls=y.second;
x->combine();
y.second=x;
}else{
y=split(x->rs,k-Size(x->ls)-);
x->rs=y.first;
x->combine();
y.first=x;
}return y;
}
inline int findkth(int k){
r Droot x=split(root,k-);
r Droot y=split(x.second,);
r node *ans=y.first;
root=merge(merge(x.first,ans),y.second);
return ans->val;
}
int getkth(node *x,int val){
if (!x) return ;
return val<x->val?getkth(x->ls,val):getkth(x->rs,val)+Size(x->ls)+;
}
inline void Insert(int val){
r int k=getkth(root,val);
r Droot x=split(root,k);
r node *a=new node(val);
root=merge(merge(x.first,a),x.second);
}
inline void Delete(int val){
r int k=getkth(root,val);
r Droot x=split(root,k-);
r Droot y=split(x.second,);
r node *ans=y.first;
root=merge(x.first,y.second);
delete ans; ans=NULL;
}
inline int prefix(int val){
r int k=getkth(root,val-);
return findkth(k);
}
inline int suffix(int val){
r int k=getkth(root,val);
return findkth(k+);
}
}
void work(){
int q=in();
while(q--){
r int op=in(),x=in();
switch(op){
case :Treap::Insert(x);break;
case :Treap::Delete(x);break;
case :printf("%d\n",Treap::getkth(Treap::root,x-)+);break;
case :printf("%d\n",Treap::findkth(x));break;
case :printf("%d\n",Treap::prefix(x));break;
case :printf("%d\n",Treap::suffix(x));break;
}
}
}
int main(){work();return ;}
【数据结构】【平衡树】无旋转treap的更多相关文章
- 无旋转Treap简介
无旋转Treap是一个神奇的数据结构,能够支持插入,删除,查询k大,查询某个数的排名,查询前驱后继,支持各种区间操作和持久化.基于旋转的Treap无法实现区间反转等操作,但是无旋Treap可以轻易地支 ...
- BZOJ3223文艺平衡树——非旋转treap
此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...
- BZOJ3224普通平衡树——非旋转treap
题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...
- Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...
- [BZOJ3223]文艺平衡树 无旋Treap
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...
- BZOJ3678 wangxz与OJ (平衡树 无旋treap)
题面 维护一个序列,支持以下操作: 1.在某个位置插入一段值连续的数. 2.删除在当前序列位置连续的一段数. 3.查询某个位置的数是多少. 题解 显然平衡树,一个点维护一段值连续的数,如果插入或者删除 ...
- 4923: [Lydsy1706月赛]K小值查询 平衡树 非旋转Treap
国际惯例的题面:这种维护排序序列,严格大于的进行操作的题都很套路......我们按照[0,k],(k,2k],(2k,inf)分类讨论一下就好.显然第一个区间的不会变化,第二个区间的会被平移进第一个区 ...
- Treap + 无旋转Treap 学习笔记
普通的Treap模板 今天自己实现成功 /* * @Author: chenkexing * @Date: 2019-08-02 20:30:39 * @Last Modified by: chenk ...
- BZOJ3223: Tyvj 1729 文艺平衡树 无旋Treap
一开始光知道pushdown却忘了pushup......... #include<cstdio> #include<iostream> #include<cstring ...
随机推荐
- 使用jmeter+ant进行接口自动化测试(数据驱动)之二:利用apache-ant执行测试用例并生成HTML格式测试报告
在 使用jmeter+ant进行接口自动化测试(数据驱动)之一 介绍了如何使用csv文件来批量管理接口 本次接着介绍如何利用apache-ant执行测试用例并生成HTML格式测试报告 ①下载安装 ap ...
- electron-vue工程创建
没有vue创建经验请移步至 vue下载与安装 使用vue创建electron-vue工程 vue init simulatedgreg/electron-vue my-project 安装elemen ...
- VMware安装时Error 1324. The path My Documents contains a invalid character的原因和解决方法
终于找到了自己想要的答案,顶顶,吼吼~ 我今天安装VMware Workstation时,总是提示我Error 1324. The path My Documents contains a inval ...
- 使用IDEA快速插入数据库数据的方法
如上图所示:数据库创建表主键使用了自增列自增因此忽略,只有后两列非主键得数据,在数据较多得时候使用IDEA快捷键Ctrl+R键,快速查找替换.
- 使用pie.htc时Border-radius的兼容
如果一个图层中(navin)使用了pie.htc来对ie6,7,8进行兼容,如若上一层(navwrap)的样式中有背景的属性,则此层 (navin) 在ie6,7,8中背景颜色不显示.如下图:此部分的 ...
- 开源软件:NoSql数据库 - 图数据库 Cassandra
转载原文:http://www.cnblogs.com/loveis715/p/5299495.html Cassandra简介 在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了 ...
- JavaScript的作用域链
/* js当中 每个函数都是一个执行环境 函数调用函数会进入新的执行环境结束之后再回来当前 作用域链: 在内部的作用域中可以访问和修改外部的变量 在外部作用域不能修改或者访问内部的变量 */ var ...
- Python之进程
进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体:在当代 ...
- Python入门之三元表达式\列表推导式\生成器表达式\递归匿名函数\内置函数
本章目录: 一.三元表达式.列表推导式.生成器表达式 二.递归调用和二分法 三.匿名函数 四.内置函数 ================================================ ...
- python 评论楼
评论楼 从数据库中取出本篇博客的所有评论使用python语句将评论整理成具有层级关系的列表 typename=request.POST.get('typename') comment_list = m ...