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

直接上代码:

//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. C Primer Plus 学习笔记 -- 前六章

    记录自己学习C Primer Plus的学习笔记 第一章 C语言高效在于C语言通常是汇编语言才具有的微调控能力设计的一系列内部指令 C不是面向对象编程 编译器把源代码转化成中间代码,链接器把中间代码和 ...

  2. 数据管理技术发展,数据库应用发展史,数据库分类,MySQL

    计算机数据管理技术发展 1. 自由管理阶段 用户以文件形式将数据组织起来,并附属在各自的应用程序下.    1.数据不保存     当时计算机主要用于科学计算,一般不需要将数据长期保存,只是计算某一课 ...

  3. netty系列之:netty对marshalling的支持

    目录 简介 netty中的marshalling provider Marshalling编码器 Marshalling编码的另外一种实现 总结 简介 在之前的文章中我们讲过了,jboss marsh ...

  4. 使用pdfcrack & crunch暴力破解PDF密码

    crunch是密码字典生成器,可以根据指定的字符来生成组合密码字典. pdfcrack是Linux下一个PDF暴力破解密工具,可以使用crunch生成的密码字典来暴力破解PDF文件的密码. 注:没有强 ...

  5. 将Abp移植进.NET MAUI项目(一):搭建项目

    ​ 前言 去年12月份做了MAUI混合开发框架的调研,想起来文章里给自己挖了个坑,要教大家如何把Abp移植进Maui项目,由于篇幅限制,将分为三个章节. 将Abp移植进.NET MAUI项目(一):搭 ...

  6. 关于一些lrzsz的知识

    问题:如何从windows轻松上传文件到Linux? 方法:容器里面:apt-get update && apt-get install lrzsz 有yum的情况:yum -y in ...

  7. React简单教程-4-事件和hook

    前言 在上一章 React 简单教程-3-样式 中我们建立了一个子组件,并稍微美化了一下.在另一篇文章 React 简单教程-3.1-样式之使用 tailwindcss 章我们使用了 tailwind ...

  8. camunda BPM支持的开发和运行环境

    以Camunda7.13版本为例,介绍Camunda支持的开发运行环境. 一.支持的Java开发环境 • Java版本: 8 / 9 / 10 / 11 / 12 / 13 / 14• Springb ...

  9. MySQL-常用数据库操作SQL汇总

    更新记录 2022年6月15日 发布. 2022年6月11日 将笔记迁移到博客中. 连接与字符设置 设置连接字符类型 SET CHARACTER SET 'utf8'; 或者 SET NAMES ut ...

  10. rosbag遍历数据出错:(unicode error) 'utf-8' codec can't decode byte 0xcd in position 31: invalid continuation byte

    主题: 前言 针对ros系统记录的bag文件,可以使用python的rosbag包,按照不同起止时间和topic进行提取. 然而,有的topic可以使用rosbag读取,但是不能遍历,存在解码错误.原 ...