大根堆与小根堆性质相比简单很多,不用加特判

直接上代码:

//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])//有子树的情况
{
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(大根堆)模板的更多相关文章

  1. Java实现堆排序(大根堆)

    堆排序是一种树形选择排序方法,它的特点是:在排序的过程中,将array[0,...,n-1]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子结点之间的内在关系,在当前无序区中选择关键 ...

  2. treap完全版模板

    这是我综合poj1442 3481 2352的treap操作 得到treap完全版模板.(经测AC) 结构体Tree { int key; //键值 int size; //该子树总节点个数 int ...

  3. bzoj4919 [Lydsy1706月赛]大根堆

    Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...

  4. bzoj 4919: [Lydsy六月月赛]大根堆

    Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...

  5. BZOJ3224/LOJ104 普通平衡树 treap(树堆)

    您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x2. 删除x(若有多个相同的数,因只删除一个)3. 查询x的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. ...

  6. Treap仿set 模板

    Treap仿set 模板 蓝书232 &代码: #include <cstdio> #include <bitset> #include <iostream> ...

  7. BZOJ4919:[Lydsy1706月赛]大根堆(set启发式合并)

    Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...

  8. 【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并

    [BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...

  9. CJOJ 2482 【POI2000】促销活动(STL优先队列,大根堆,小根堆)

    CJOJ 2482 [POI2000]促销活动(STL优先队列,大根堆,小根堆) Description 促销活动遵守以下规则: 一个消费者 -- 想参加促销活动的消费者,在账单下记下他自己所付的费用 ...

随机推荐

  1. CRM项目的整理---第一篇

    CRM:cunstomer relationship management  客户管理系统 1.项目的使用者:销售  班主任    讲师  助教 2.项目的需求分析 2.1.注册 2.2.登录 2.3 ...

  2. wsgiref模块、web框架、django框架简介

    """web框架:将前端.数据库整合到一起的基于互联网传输的python代码 web框架也可以简单的理解为是软件开发架构里面的'服务端'""" ...

  3. 【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack

    前景回顾 [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]从零开始实现 mq-02-如何实现生产者调用消费者? [mq]从零开始实现 mq-03-引入 broker 中间人 [mq]从零 ...

  4. Angular中懒加载一个模块并动态创建显示该模块下声明的组件

    angular中支持可以通过路由来懒加载某些页面模块已达到减少首屏尺寸, 提高首屏加载速度的目的. 但是这种通过路由的方式有时候是无法满足需求的. 比如, 点击一个按钮后显示一行工具栏, 这个工具栏组 ...

  5. linux篇-linux awstats搭建

    1安装lnmp模式 2安装awstats 2# tar xf awstats-6.5.tar.gz 解压下载到/root下的压缩包 mkdir /var/lib/awstats chmod 755 / ...

  6. mysql配置与存储引擎与字段类型与约束条件

    目录 字符编码与配置文件 存储引擎 创建表的完整语法 字段类型 整型 浮点型 字符类型 数字的含义 枚举与集合 日期类型 约束条件 字符编码与配置文件 在MySQL5.X系列中,显示的字符编码有多种, ...

  7. python之模块(hashlib、logging)

    hashlib模块 加密的概念 加密,是以某种特殊的算法改变原有的数据,当其他人获得数据时,也无法了解数据的内容.简单的来说,就是将明文(人看得懂)数据通过一些手段变成密文数据(人看不懂),密文数据的 ...

  8. Fail2ban 使用Fail2ban监禁SSH服务的恶意IP

    Fail2ban自带了很多服务的过滤器(filter)和动作(action),它已经帮你做好了,所以一般情况下我们无需定义,直接引用即可. 这边只是一个示例. 系统版本:Ubuntu 16.04.5 ...

  9. 什么!Sentinel流控规则可以这样玩?

    项目源码地址:公众号回复 sentinel,即可免费获取源码 前言 上一篇文章中,我们讲解了关于sentinel基本介绍以及流控规则中直接和快速失败的效果,有兴趣的可以去看上一篇文章,今天,我们给大家 ...

  10. [XJOI3529] 左右

    题目链接:左右 Description 给你一个s数组,一个t数组,你可以对s数组执行以下两种操作 L 操作:每个数等于其左边的数加上自己 R 操作:每个数等于其右边的数加上自己 第一个数的左边是最后 ...