【洛谷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]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...
随机推荐
- 富文本编辑器 CKeditor 配置使用 (带附件)
Ckeditor下载地址:http://ckeditor.com/download 1.CKeditor的基本配置 var textval=CKEDITOR.instances.TextArea1.g ...
- Game Engine Architecture 5
[Game Engine Architecture 5] 1.Memory Ordering Semantics These mysterious and vexing problems can on ...
- Go语言编程读书笔记:Go channel(1)
Channel是Go语言在语言级别提供的goroutine间的通信方式.我们可以用channel在两个或多个goroutine之间传递消息.channel是进程内的通信方式,因此通过channel传递 ...
- Tensorflow学习笔记2019.01.03
tensorflow学习笔记: 3.2 Tensorflow中定义数据流图 张量知识矩阵的一个超集. 超集:如果一个集合S2中的每一个元素都在集合S1中,且集合S1中可能包含S2中没有的元素,则集合S ...
- excle 内部 超链接(锚点)
超连接对象: 1.文档 2.本文档中的位置. 3. 本文重点 指定 链接到 xx表中的xx位置. 第三种连接 类似于 web文档的中 锚点 超连接 看下图 选 择本文档中的位置, 选择 工作表. ...
- composer的安装以及具体使用
1. 简介 Composer 是 PHP5.3以上 的一个依赖管理工具.它允许你声明项目所依赖的代码库,它会在你的项目中为你安装他们.Composer 不是一个包管理器.是的,它涉及 "pa ...
- stark组件开发之组合搜索实现思路
- 关键字搜索. 可以做到的效果是, 输入20. 后太通过 Q() 函数. 来实现. 搜索是一个大的问题点. - 要想实现组合搜索, 首先要 明确的一点是. 在我当前的页面上, 正在进行展示的是 ...
- ERC20 token standard issues.
- C#使用反射获取对象变化的情况
记录日志时, 经常需要描述对象的状态发生了怎样的变化, 以前处理的非常简单粗暴: a. 重写class的ToString()方法, 将重要的属性都输出来 b. 记录日志时: 谁谁谁 由 变更前实 ...
- linux 学习之路:ls命令使用
1.命令格式: Ls 显示指定工作目录下的目录内容 语法:ls [-alrtAFR] [name...] 2.命令参数: 3.命令功能: 显示列出目前工作目录所含之文件及子目录 4.命令范例: 列出根 ...