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

直接上代码:

//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. 『现学现忘』Git基础 — 21、git diff命令

    目录 1.git diff 命令说明 2.比较工作区与暂存区中文件的差别 3.比较暂存区与本地库中文件的差别 4.总结git diff命令常见用法 5.总结 1.git diff 命令说明 在comm ...

  2. range内部代码

    def my_range(a, b=None, c=1): if not b: b = a a = 0 while a < b: yield a a += c

  3. node-sass,sass-loader和node之间的关系

    vue-cli运行在node平台上scss语言是运行在 node-sass平台上node-sass的运行环境是node平台vue-cli工程中不识别scss语法,.scss模块,sass-loader ...

  4. vue3常见问题及解决方案(四)父组件切换行,然后子组件切换tab,子组件内的数据不刷新

    问题描述 父组件切换行,然后子组件切换tab,子组件内的数据不刷新. 例如父组件为订单,子组件为订单相关商品和相关客户,商品和客户使用tab选项卡组织. 当tab显示商品页时,切换订单,商品页内容跟着 ...

  5. MySQL的Explain总结

    Explain简介 MySQL优化器在基于成本的计算和基于规则的SQL优化会生成一个所谓的执行计划,我们就可以使用执行计划查看MySQL对该语句具体的执行方式. 介绍这个好啰嗦就是了,我们可以通过这个 ...

  6. jupyter notebook修改默认浏览器

    1. anaconda集成了python以及各种库.python和anaconda可二选一. 2. anaconda或python安装后记得把pip源改为国内的镜像源地址.比如163,阿里,清华以及南 ...

  7. c++ 树状数组

    关于树状数组 树状数组,即 Binary Indexed Tree ,主要用于维护查询前缀和 属于 log 型数据结构 和线段树比较 都是 log 级别 树状数组常数.耗费的空间.代码量都比线段树小 ...

  8. 大功率超远距离lora无线数传电台,多级中继功能

    一.在无线通信领域,LoRa是目前市场最被看好的技术之一.随着新一代LoRa调制技术的升级,市场对LoRa技术的认知.认可逐步提高,基于LoRa调制技术开发的产品得到更广泛的应用.受益于其超低的接收灵 ...

  9. 【Java面试】请简单说一下你对受检异常和非受检异常的理解

    Hi,我是Mic 今天给大家分享一道阿里一面的面试题. 这道题目比较基础,但是确难倒了很多人. 关于"受检异常和非受检异常的理解" 我们来看看普通人和高手的回答. 普通人: 嗯.. ...

  10. alertmanager集群莫名发送resolve消息的问题探究

    alertmanager集群莫名发送resolve消息的问题探究 术语 告警消息:指一条告警 告警恢复消息:指一条告警恢复 告警信息:指告警相关的内容,包括告警消息和告警恢复消息 问题描述 最近遇到了 ...