【洛谷P3369】【模板】普通平衡树题解

题目链接

题意:

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

输入格式:

第一行为n,表示操作的个数。下面n行每行有两个整数opt和x,opt表示操作的序号

输出格式:

对于操作3,4,5,6每行输出一个数,表示对应答案

样例输入:


样例输出:


时空限制:

每个测试点1s,128MB

数据范围:

n≤105

|x|≤107

1≤opt≤6

题解:

首先来讲讲什么是Treap……

Treap的中文名叫树堆(Tree+Heap),同时拥有权值和优先级;对于权值来说,它是一棵二叉搜索树(BST);对于优先级来说,它是一个堆。它可以被当作平衡树来用。

我们希望平衡树的高度尽量小,因为只有这样才能使每次操作的均摊时间复杂度趋于O(logn)。为了避免极端数据使平衡树退化为链,导致时间复杂度退化为O(n),我们可以想到用优先级来维护Treap的形态使它的高度尽量趋于平衡,因为当每个节点的权值和优先级确定时,Treap的形态是唯一确定的。我们把每个节点的优先级用随机数rand()来赋值,这样Treap就趋于平衡了。

详细代码如下(指针实现):

 #include<cstdio>
#include<ctime>
#include<cstdlib>
struct node{
node*ch[];//指向左(ch[0])右(ch[1])子节点的指针
int v;//权值
int r;//优先级
int cnt;//该节点数的个数(数可以重复)
int size;//以该节点为根的子树中数的个数
node(int vv){//新建节点的构造函数
ch[]=ch[]=NULL;//两指针初始化为空指针
v=vv;//权值初始化
r=rand();//优先级用随机数初始化
cnt=size=;//只含一个数
}
};
node*super;//指向整棵Treap的大根的指针
int n,ans;
inline void maintain(node*p){//维护该节点的附加信息
p->size=p->cnt;
if(p->ch[]!=NULL)p->size+=p->ch[]->size;
if(p->ch[]!=NULL)p->size+=p->ch[]->size;
}
inline void rot(node*&p,int lr){//旋转操作(lr==0:左旋,lr==1:右旋)
node*k=p->ch[lr^];
p->ch[lr^]=k->ch[lr];
k->ch[lr]=p;
p=k;
}
void ins(node*&p,int val){//插入操作:在以p为根的子树中插入一个val数
if(p==NULL){//空节点直接建新点
p=new node(val);
}else if(val==p->v){//找到了已经存在的节点
p->cnt++;
p->size++;
}else if(val<p->v){
ins(p->ch[],val);//在左子节点中插入
if(p->ch[]->r<p->r){//维护优先级(小根堆)
rot(p,);
maintain(p->ch[]);
}
maintain(p);
}else{
ins(p->ch[],val);//在右子节点中插入
if(p->ch[]->r<p->r){
rot(p,);
maintain(p->ch[]);
}
maintain(p);
}
}
void del(node*&p,int val){//删除操作:在以p为根的子树中删除一个val数
if(val<p->v){
del(p->ch[],val);
maintain(p);
}else if(val>p->v){
del(p->ch[],val);
maintain(p);
}else{
if(p->cnt>){//该节点包含多个数,直接减即可
p->cnt--;
p->size--;
}else{//删除该节点
if(p->ch[]!=NULL){
if(p->ch[]!=NULL){//该节点有左右子节点
int lr=p->ch[]->r<p->ch[]->r?:;//选优先级小的子节点为根(小根堆)
rot(p,lr^);//旋转
del(p->ch[lr^],val);//递归向下删除
maintain(p);//递归返回向上维护
}else{//无右子节点
node*pp=p;
p=p->ch[];
delete pp;
}
}else{//无左子节点
if(p->ch[]!=NULL){
node*pp=p;
p=p->ch[];
delete pp;
}else{//都无
delete p;
p=NULL;
}
}
}
}
}
int qrank(node*p,int val){//当前以p为根的子树中小于val的数的个数+1
int ssl=p->ch[]==NULL?:p->ch[]->size;//该子树中小于val的数的个数
if(val==p->v)return ssl+;
else if(val<p->v){
if(p->ch[]!=NULL)return qrank(p->ch[],val);
else return ;
}else{
if(p->ch[]!=NULL)return ssl+p->cnt+qrank(p->ch[],val);
else return p->size+;
}
}
int qval(node*p,int rank){//在当前子树中查找排名为rank的数
int ssl=p->ch[]==NULL?:p->ch[]->size;
if(rank<=ssl)return qval(p->ch[],rank);
else if(rank<=ssl+p->cnt)return p->v;
else return qval(p->ch[],rank-ssl-p->cnt);
}
void pred(node*p,int val){//在当前子树中查找val的前驱
if(val<=p->v){
if(p->ch[]!=NULL)pred(p->ch[],val);
}else{
ans=p->v;//ans为暂定的答案,当ans无法再修改时,ans为最终答案
if(p->ch[]!=NULL)pred(p->ch[],val);
}
}
void succ(node*p,int val){
if(val>=p->v){
if(p->ch[]!=NULL)succ(p->ch[],val);
}else{
ans=p->v;
if(p->ch[]!=NULL)succ(p->ch[],val);
}
}
int main(){
srand(time());//初始化随机数种子
scanf("%d",&n);
while(n--){
int opt,x;
scanf("%d%d",&opt,&x);
switch(opt){
case :{
ins(super,x);
break;
}
case :{
del(super,x);
break;
}
case :{
printf("%d\n",qrank(super,x));
break;
}
case :{
printf("%d\n",qval(super,x));
break;
}
case :{
pred(super,x);
printf("%d\n",ans);
break;
}
case :{
succ(super,x);
printf("%d\n",ans);
break;
}
}
}
return ;
}

【洛谷P3369】【模板】普通平衡树题解的更多相关文章

  1. 【洛谷P3369】普通平衡树——Splay学习笔记(一)

    二叉搜索树(二叉排序树) 概念:一棵树,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉搜索树 ...

  2. 洛谷.3369.[模板]普通平衡树(Splay)

    题目链接 第一次写(2017.11.7): #include<cstdio> #include<cctype> using namespace std; const int N ...

  3. 洛谷.3369.[模板]普通平衡树(fhq Treap)

    题目链接 第一次(2017.12.24): #include<cstdio> #include<cctype> #include<algorithm> //#def ...

  4. 洛谷.3391.[模板]文艺平衡树(Splay)

    题目链接 //注意建树 #include<cstdio> #include<algorithm> const int N=1e5+5; //using std::swap; i ...

  5. 洛谷P3369 【模板】普通平衡树(Treap/SBT)

    洛谷P3369 [模板]普通平衡树(Treap/SBT) 平衡树,一种其妙的数据结构 题目传送门 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除 ...

  6. 洛谷P3369普通平衡树(Treap)

    题目传送门 转载自https://www.cnblogs.com/fengzhiyuan/articles/7994428.html,转载请注明出处 Treap 简介 Treap 是一种二叉查找树.它 ...

  7. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

  8. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  9. 洛谷P4047 [JSOI2010]部落划分题解

    洛谷P4047 [JSOI2010]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...

随机推荐

  1. Linux中 ./configure --prefix命令

    源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make install),具体的安装方法一般作者都会给出文档,这里主要讨论配置(configure).Configu ...

  2. Java框架spring Boot学习笔记(八):@PropertySource,@Value注解

    获取配置文件 忽略配置文件不存在时报错

  3. PHP 构造方法 __construct()和PHP 析构方法 __destruct()

    PHP 构造方法 __construct() 允许在实例化一个类之前先执行构造方法. 构造方法 构造方法是类中的一个特殊方法.当使用 new 操作符创建一个类的实例时,构造方法将会自动调用,其名称必须 ...

  4. 拦截过滤防御XSS攻击 -- Struts2.3 以及 2.5 的解决方式

    使用Struts2框架开发的后台在防御XSS攻击的时候很多方式都不能用,因为Struts2对请求进行的二次封装有区别.以下针对Struts2的XSS攻击进行拦截过滤防御解决: Struts2.3 本方 ...

  5. Appium 学习三:Appium-Desktop 填坑

    之前使用appium执行自动化脚本,现使用appium-desktop  遇到了一些问题 1.使用name定位的报错   Locator Strategy 'name' is not supporte ...

  6. python note 11 函数名的使用、闭包、迭代器

    1.函数名就是一个变量 def func(): print("我是一个小小的函数") a = func print(a) #输出变量存放地址 <function func a ...

  7. php 积分抽奖活动(大转盘)

    以下是项目代码(公众号,使用积分进行抽奖活动),只可做参考: public function Sncode(){ $tid = I('request.tid', 0, 'intval'); // 大转 ...

  8. IDEA 开发环境中设置Subversion,遇到svn安装路径包含空格无法使用版本控制的解决办法

    假如你的svn.exe的安装位置是:C:\Program Files\TortoiseSVN\bin\svn.exe,路径中包含空格. 1.File->Settings->Version ...

  9. 转 Java操作PDF之iText详细入门

    转 Java操作PDF之iText详细入门 2016年08月08日 11:06:00 阅读数:19490 iText是著名的开放项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成 ...

  10. appium定位toast消息的使用

    定位使用xpath后,定位消息文本,然后使用text获取消息文本做断言.toast_loc = ("xpath", ".//*[contains(@text,'切换运营商 ...