treap(小根堆)模板
总结教训
对于treap使用小根堆性质,一定要特判左右子树是否存在,因为空节点的优先级为0,是最高的,不特判会出错我就这么错了,so
一定要特判!一定要特判!一定要特判!重要的事情说三遍
本文代码根据P3369 【模板】普通平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)写的
模板,有注释:
//treap(小根堆性质)
#include<bits/stdc++.h>
#define rint register int
typedef long long ll;
using namespace std;
inline ll read()//快读
{
ll x=0;
bool fg=false;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') fg=true;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return fg?~x+1:x;
}
const int N=1e6+5;
const ll INF=1e9+5;
int n,tot,root;//n 操作次数 tot 记录节点的标号 root 根节点
int ch[N][2];//左右孩子
ll val[N],pai[N],cnt[N],siz[N];
//val 数值 pai 存储优先级 cnt 该数值出现了几次 siz 大小
inline void pushup(int id)//更新siz
{
siz[id]=siz[ch[id][0]]+siz[ch[id][1]]+cnt[id];
//更新,该节点的大小=左子树的大小+右子树的大小+自己出现了几次
}
int New(ll v)//插入新节点
{
val[++tot]=v;
pai[tot]=rand();
//随机函数,随即赋予一个数值,头文件<stdlib.h>
cnt[tot]++;//cnt[tot]=1;
siz[tot]++;//siz[tot]=1;
return tot;
}
void spin(int &id,int d)
//旋转 id 要旋转的节点编号,因为会改变,所以要加& d(direction) 旋转方向
{
int temp=ch[id][d^1];//记录与旋转方向相反的子节点,这里画图自己理解一下
ch[id][d^1]=ch[temp][d];//先存下来
ch[temp][d]=id;//再修改
pushup(id);//先更新原节点
pushup(temp);//再更新旋转后的节点
id=temp;//最后更新id,以便其他操作需要
}
void build()
//初始化,这两个点一个在最左下角,一个在最右下角,这里最左下角的会影响后面有关排名的的查询
{
root=New(-INF);
ch[root][1]=New(INF);
if(pai[root]>pai[ch[root][1]]) spin(root,0);//不符合小根堆性质,就左旋
}
void insert(int &id,ll x)//插入节点
{
if(id==0)//如果已经到叶子节点了,直接插入新节点
{
id=New(x);
return;
}
if(x==val[id]) cnt[id]++;//如果和该节点数值相等,cnt(出现次数)+1
else
{
int d=x<val[id]?0:1;//判断是属于左子树还是右子树
insert(ch[id][d],x);
if(pai[ch[id][d]]<pai[id]) spin(id,d^1);//要符合小根堆的性质
}
pushup(id);//更新大小
}
void del(int &id,ll x)
{
if(!id) return;//如果没有这个点,直接返回
//这里else不能少,因为id是会改变的
if(x==val[id])//就是该节点,进行特判
{
if(cnt[id]>1) cnt[id]--;//出现多次,减一即可
else//这里小根堆一定要进行特判子树,因为空节点的优先级最高,会报错
{
if(ch[id][0]&&!ch[id][1])//只有左子树,没有右子树
spin(id,1),del(ch[id][1],x);
else
{
if(!ch[id][0]&&ch[id][1])//只有右子树,没有左子树
spin(id,0),del(ch[id][0],x);
else
{
if(ch[id][0]&&ch[id][1])//左右子树都有
{
int d=pai[ch[id][0]]<pai[ch[id][1]]?1:0;//判断优先级
spin(id,d);//旋转
del(ch[id][d],x);//删除
}
else id=0;//左右子树都没有
}
}
}
}
else//如果不是该节点,向子树出发
{
int d=x<val[id]?0:1;
del(ch[id][d],x);
}
pushup(id);//更新大小
}
ll get_rank(int id,ll x)//找x数(这里是数字)的排名(注意,是排名)
{
if(!id) return 0;//如果没有这个数,返回0
if(x<val[id]) return get_rank(ch[id][0],x);
//比当前节点小,向左子树中找
if(x>val[id]) return siz[ch[id][0]]+cnt[id]+get_rank(ch[id][1],x);
//比当前节点大,向右子树中找
return siz[ch[id][0]]+1;
//不大不小,则就是该节点,要返回这个点左子树的大小,并加上自己(也就是+1)
}
ll get_val(int id,ll x)//找排名为x的数(注意,是数字)
{
if(!id) return 0;//如果该节点为空,返回0
if(x<=siz[ch[id][0]]) return get_val(ch[id][0],x);
//如果排名小于等于左子树的大小,就说明排名的这个位置在左子树中
if(x>siz[ch[id][0]]&&x<=siz[ch[id][0]]+cnt[id]) return val[id];
//如果大于左子树大小,小于等于左子树大小+当前节点的大小,则就是该节点
return get_val(ch[id][1],x-siz[ch[id][0]]-cnt[id]);
//以上都不符合,就是在右子树,这里x要减去左子树大小和当前节点的出现次数
}
ll get_pre(ll x)//求前驱
{
int id=root;
ll pre;
while(id)
{
if(val[id]<x)//如果当前节点比x小,记录答案,去右子树中搜更优值
{
pre=val[id];//记录答案
id=ch[id][1];//id转到右子树
}
else id=ch[id][0];//否则,在左子树中搜
}
return pre;
}
ll get_nxt(ll x)//求后继
{
int id=root;
ll nxt;
while(id)
{
if(val[id]>x)//如果当前节点比x大,记录答案,去左子树中搜更优值
{
nxt=val[id];//记录答案
id=ch[id][0];//id转到左子树
}
else id=ch[id][1];//否则,在右子树中搜
}
return nxt;
}
int main()
{
build();//初始化
n=read();//读入操作次数
for(rint i=1;i<=n;++i)
{
int op=read();
ll x=read();
switch(op)
{
case 1:
insert(root,x);
break;
case 2:
del(root,x);
break;
case 3:
printf("%lld\n",get_rank(root,x)-1);
break;
case 4:
printf("%lld\n",get_val(root,x+1));
break;
case 5:
printf("%lld\n",get_pre(x));
break;
case 6:
printf("%lld\n",get_nxt(x));
break;
}
}
return 0;
}
大根堆模板链接:treap(大根堆)模板 - yi_fan0305 - 博客园 (cnblogs.com)
treap(小根堆)模板的更多相关文章
- 洛谷 P3378 【模板】堆(小根堆)
题目描述 如题,初始小根堆为空,我们需要支持以下3种操作: 操作1: 1 x 表示将x插入到堆中 操作2: 2 输出该小根堆内的最小数 操作3: 3 删除该小根堆内的最小数 输入输出格式 输入格式: ...
- P3378 堆の模板
如果不是可并堆/带修堆/卡常题,一般都用优先队列实现. 很多O(nlogn)过不了的题都可以用蚯蚓的套路来实现!!! 优先队列带修用延迟删除法. 堆,可以简单的用优先队列来实现,也可以自己手打. #i ...
- 关于dijkstra的小根堆优化
YY引言 在NOI2018D1T1中出现了一些很震惊的情况,D1T1可以用最短路解决,但是大部分人都在用熟知的SPFA求解最短路.而SPFA的最坏复杂度能够被卡到$O(VE)$.就是边的数量乘以点的数 ...
- 可旋转Treap(树堆)总结
树堆,在数据结构中也称Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树.其基本操作的期望时间复杂度为O(logn).相对于其他的平衡二叉搜索树,Trea ...
- treap完全版模板
这是我综合poj1442 3481 2352的treap操作 得到treap完全版模板.(经测AC) 结构体Tree { int key; //键值 int size; //该子树总节点个数 int ...
- scala写算法-用小根堆解决topK
topK问题是指从大量数据中获取最大(或最小)的k个数,比如从全校学生中寻找成绩最高的500名学生等等. 本问题可采用小根堆解决.思路是先把源数据中的前k个数放入堆中,然后构建堆,使其保持堆序(可以简 ...
- 让priority_queue支持小根堆的几种方法
点击这里了解什么是priority_queue 前言 priority_queue默认是大根堆,也就是大的元素会放在前面 例如 #include<iostream> #include< ...
- T-shirt buying CodeForces - 799B (小根堆+STL)
题目链接 思路: 由于题目说了只有1,2,3,三种色号的衣服,然后开三个对应色号的小根堆, 我是根据pair<int,int> 创建了一个以价格小的优先的优先队列. pair中的另外一个i ...
- BZOJ3224/LOJ104 普通平衡树 treap(树堆)
您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x2. 删除x(若有多个相同的数,因只删除一个)3. 查询x的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. ...
随机推荐
- 逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
什么是 AST AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构. ...
- logging、openpyxl、第三方模块下载
### 日志模块的组成部分 ```pythonimport logging# 1.logger对象:产生日志logger = logging.getLogger('转账记录')# 2.filter对象 ...
- 低代码 —— 初步认识 Appsmith
初步认识 Appsmith appsmith 是什么 appsmith 是 github 上的一个开源项目,截至此刻(20220512)有 17.7k Star. Appsmith 是一个低代码.开源 ...
- unity---2d游戏杂记
2d游戏制作的笔记 save Layout 增加配置 Packges文件夹 插件 调整视野 鼠标中键 拉近拉远 鼠标右键 平移 Alt+鼠标左键 移动视角 Pivot/Center 当前物体中心和多个 ...
- 浅谈BSGS和EXBSGS
我的 BSGS 和各位犇犇的差不多,但是不需要求逆元 Luogu [ TJOI2007 ] 可爱的质数 原题展现 题目描述 给定一个质数 \(p\),以及一个整数 \(b\),一个整数 \(n\),现 ...
- 解锁!玩转 HelloGitHub 的新姿势
本文不会涉及太多技术细节和源码,请放心食用 大家好,我是 HelloGitHub 的老荀,好久不见啊! 我在完成 HelloZooKeeper 系列之后,就很少"露面了".但是我对 ...
- 小白excel初步使用2022.06.02
1.添加 对表格数据相加求和:在表示数据的那一列黄色表格下输入=SUM(D1:D5)或者alt+=或者SUMIF(D1:D5,">50")或者SUMIF(D1,D3:D7,1 ...
- 更换conda镜像源、pip镜像源
镜像源一般有两点特别需要注意,一个是Conda源,一个是Pip源: 更换Conda源,以更换清华Conda源为例: Anaconda 镜像使用帮助 Anaconda 是一个用于科学计算的 Python ...
- LVS+keepalived简单搭建(二)
在LVS1的基础上进行搭建 https://www.cnblogs.com/hikoukay/p/12860476.html keeplived主机 用node01,node04两台 先清掉原先nod ...
- WPF开发随笔收录-DrawingVisual绘制高性能曲线图
一.前言 项目中涉及到了心率监测,而且数据量达到了百万级别,通过WPF实现大数据曲线图时,尝试过最基础的Canvas来实现,但是性能堪忧,而且全部画出来也不实际.同时也尝试过找第三方的开源库,但是因为 ...