【洛谷P3369】【模板】普通平衡树题解
【洛谷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】【模板】普通平衡树题解的更多相关文章
- 【洛谷P3369】普通平衡树——Splay学习笔记(一)
二叉搜索树(二叉排序树) 概念:一棵树,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉搜索树 ...
- 洛谷.3369.[模板]普通平衡树(Splay)
题目链接 第一次写(2017.11.7): #include<cstdio> #include<cctype> using namespace std; const int N ...
- 洛谷.3369.[模板]普通平衡树(fhq Treap)
题目链接 第一次(2017.12.24): #include<cstdio> #include<cctype> #include<algorithm> //#def ...
- 洛谷.3391.[模板]文艺平衡树(Splay)
题目链接 //注意建树 #include<cstdio> #include<algorithm> const int N=1e5+5; //using std::swap; i ...
- 洛谷P3369 【模板】普通平衡树(Treap/SBT)
洛谷P3369 [模板]普通平衡树(Treap/SBT) 平衡树,一种其妙的数据结构 题目传送门 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除 ...
- 洛谷P3369普通平衡树(Treap)
题目传送门 转载自https://www.cnblogs.com/fengzhiyuan/articles/7994428.html,转载请注明出处 Treap 简介 Treap 是一种二叉查找树.它 ...
- 洛谷P1783 海滩防御 分析+题解代码
洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 洛谷P4047 [JSOI2010]部落划分题解
洛谷P4047 [JSOI2010]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...
随机推荐
- 记一次yii2 上传文件
1 view渲染 <form action="../src/website/import/report-flow" method="post" encty ...
- Ubuntu 清除缓存 apt-get命令参数
整理了Ubuntu Linux操作系统下apt-get命令的详细说明,分享给大家.常用的APT命令参数:apt-cache search package 搜索包apt-cache show packa ...
- 算法练习LeetCode初级算法之设计问题
打乱数组 不断的让第一个与后面随机选择的数交换 class Solution { private int[] nums; private int[] initnums; public Solution ...
- vue项目性能优化,优化项目加载慢的问题
一. 对路由组件进行懒加载: 如果使用同步的方式加载组件,在首屏加载时会对网络资源加载加载比较多,资源比较大,加载速度比较慢.所以设置路由懒加载,按需加载会加速首屏渲染.在没有对路由进行懒加载时,在C ...
- BootStrap插件
站点引用 Bootstrap 插件的方式有两种: 单独引用:使用 Bootstrap 的个别的 *.js 文件.一些插件和 CSS 组件依赖于其他插件.如果您单独引用插件,请先确保弄清这些插件之间的依 ...
- 转*SqlSever查询某个表的列名称、说明、备注、注释,类型等
@原文地址 关键部分如下: ------sqlserver 查询某个表的列名称.说明.备注.类型等 SELECT 表名 then d.name else '' end, 表说明 then isnull ...
- VS2015一新建项目就出现未将对象引用设置到对象的实例怎么办?[z]
https://blog.csdn.net/tiandyoin/article/details/79722800 在控制面板-卸载或修复程序太麻烦,而且不一定保证解决,可以这样------打开--C: ...
- ROS零门槛学渣教程系列(二)——Linux常用指令:mkdir、tar、 unzip、cp、scp、mv、rm、find、apt、ssh
Linux常用指令通过上一教程,我们获得了ubuntu系统.Linux是一个很大的领域.但不要紧张,我们一步步来就是了,跟着教程,需要能用到新知识,会提前介绍给大家.下面学习几个常用的Linux指令. ...
- 使用react-handsontable
新建一个项目 create-react-app myProject cd myProject npm install handsontable 或者 npm install handsontable- ...
- sed awk
sed -n 's/,*$//g;s/,\+/,/g;/,/p' test.csv 去除行尾逗号,将多个连续逗号合并,过滤没有逗号的行 awk -F, 'NF>5 split($1,a,&quo ...