本文内容部分转自某大佬博客:https://blog.csdn.net/CABI_ZGX/article/details/79963427

例题:https://www.luogu.org/problemnew/show/P3369#sub

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入 x 数
  2. 删除 x 数(若有多个相同的数,因只删除一个)
  3. 查询 x 数的排名(排名定义为比当前数小的数的个数 +1 。若有多个相同的数,因输出最小的排名)
  4. 查询排名为 x 的数
  5. 求 x 的前驱(前驱定义为小于 x ,且最大的数)
  6. 求 x 的后继(后继定义为大于 x ,且最小的数)

输入输出格式

输入格式:

第一行为 n ,表示操作的个数,下面 n 行每行有两个数 opt 和 x ,opt 表示操作的序号( 1≤opt≤6 )

输出格式:

对于操作 3,4,5,6 每行输出一个数,表示对应答案

看题目就知道Treap当然是可以的,考虑fhq-treap

fhq-treap的最大特点,不需要不需要不需要旋转!!!这是棵不考旋转保持平衡的平衡树

不管怎么样,我们要让它是平衡的。好的其实有些除旋转之外的神奇的操作是有效的,下面来讲一讲。

对于随机数我们维护小根堆;对于权值左儿子的权值小于当前节点的权值,右儿子的权值大于当前节点的权值

Mergy

merge就是把两个treap合成一个,是一个递归的过程。首先明确fhq-treap的平衡条件是他的附加权值,那么只需要比较他们的附加权值大小判断在左树还是右树即可。

注意x树里的所有点的权值小于y树里的所有点的权值

inline int mergy(int x,int y)
{
if (!x||!y) return x|y;
if (a[x].rnd<a[y].rnd)
{
a[x].son[]=mergy(a[x].son[],y);
update(x);
return x;
}
else
{
a[y].son[]=mergy(x,a[y].son[]);
update(y);
return y;
}
}

Split

split是把一颗treap分开两个树的操作,也是一个递归的过程。有两种分法,一种是按权值分,一种是按size(子树大小)分。

按权值分(注意这时候权值小于等于 k的节点都在左树中,大于k的都在右树中)

void split(int now,int k,int &x,int &y)
{
if (!now)
{
x=y=;
return;
}
if (k>=a[now].dat)
{
x=now;
split(a[now].son[],k,a[now].son[],y);
}
else
{
y=now;
split(a[now].son[],k,x,a[now].son[]);
}
update(now);
return;
}

按size分

void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
update(now);
if (k<=tr[tr[now].son[]].size)
y=now,split(tr[now].son[],k,x,tr[now].son[]);
else
x=now,split(tr[now].son[],k-tr[tr[now].son[]].size-,tr[now].son[],y);
update(now);
}
}

New,建立新的节点

int New(int x)
{
tot++;
a[tot].dat=x;a[tot].rnd=rand();a[tot].size=;
return tot;
}

找kth的节点的权值

int kth(int now,int k)
{
while ()
{
if (k<=a[a[now].son[]].size) now=a[now].son[];
else
{
if (k==a[a[now].son[]].size+) return now;
else
{
k=k-(a[a[now].son[]].size+);
now=a[now].son[];
}
}
}
}

Insert

插入一个权值为t节点,那么只需要以t为权值split整棵树,然后New(t)在merge回去

split(root,t,x,y);
root=mergy(mergy(x,New(t)),y);

Del

删除权值为t的点,先把整颗树以t为权值split成两棵树x,z,再把x树按照t-1分成x,y。这时候值为t的点一定为y的根,那么我们把y的两个子儿子merge起来,再把他们重新merge起来得到一个新的树,这颗树就去除掉了t。

split(root,t,x,z);
split(x,t-,x,y);
y=mergy(a[y].son[],a[y].son[]);
root=mergy(mergy(x,y),z);

FindRank

找值为t的节点排名,就把整棵树以t-1为权值split,那么小于t的节点全在左树内,输出左树的size即可

precursor

找前驱就把整棵树按t-1为权值split开,此时小于等于t的数全在左树里,在左树里找到排名为(左树的size)的节点权值即可。

split(root,t-,x,y);
printf("%d\n",a[kth(x,a[x].size)].dat);
root=mergy(x,y);

successor

找后继是相同的,把整棵树按t为权值split开,此时右树排名第一的数就是后继

split(root,t,x,y);
printf("%d\n",a[kth(y,)].dat);
root=mergy(x,y);

fhq-treap代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<time.h>
using namespace std; const int maxn=1e5+;
int n,tot,root;
struct fhq
{
int son[];
int rnd,dat,size;
}a[maxn];
inline int read()
{
char ch=getchar();
int s=,f=;
while (!(ch>=''&&ch<='')) {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
return s*f;
}
inline void update(int x)
{
a[x].size=a[a[x].son[]].size+a[a[x].son[]].size+;
return;
}
int New(int x)
{
tot++;
a[tot].dat=x;a[tot].rnd=rand();a[tot].size=;
return tot;
}
inline int mergy(int x,int y)
{
if (!x||!y) return x|y;
if (a[x].rnd<a[y].rnd)
{
a[x].son[]=mergy(a[x].son[],y);
update(x);
return x;
}
else
{
a[y].son[]=mergy(x,a[y].son[]);
update(y);
return y;
}
}
void split(int now,int k,int &x,int &y)
{
if (!now)
{
x=y=;
return;
}
if (k>=a[now].dat)
{
x=now;
split(a[now].son[],k,a[now].son[],y);
}
else
{
y=now;
split(a[now].son[],k,x,a[now].son[]);
}
update(now);
return;
}
int kth(int now,int k)
{
while ()
{
if (k<=a[a[now].son[]].size) now=a[now].son[];
else
{
if (k==a[a[now].son[]].size+) return now;
else
{
k=k-(a[a[now].son[]].size+);
now=a[now].son[];
}
}
}
}
int main()
{
srand(time());
n=read();
while (n--)
{
int opt=read(),t=read();
int x,y,z;
if (opt==)
{
split(root,t,x,y);
root=mergy(mergy(x,New(t)),y);
}
if (opt==)
{
split(root,t,x,z);
split(x,t-,x,y);
y=mergy(a[y].son[],a[y].son[]);
root=mergy(mergy(x,y),z);
}
if (opt==)
{
split(root,t-,x,y);
printf("%d\n",a[x].size+);
root=mergy(x,y);
}
if (opt==)
{
int k=kth(root,t);
printf("%d\n",a[k].dat);
}
if (opt==)
{
split(root,t-,x,y);
printf("%d\n",a[kth(x,a[x].size)].dat);
root=mergy(x,y);
}
if (opt==)
{
split(root,t,x,y);
printf("%d\n",a[kth(y,)].dat);
root=mergy(x,y);
}
}
return ;
}

Treap代码:

#include<bits/stdc++.h>
using namespace std; const int maxn=1e5+;
const int inf=1e9+;
int n,m,tot,root;
struct TREAP
{
int l,r;
int val,dat;
int cnt,size;
}a[maxn];
inline void read(int &x)
{
char ch=getchar();
int s=,f=;
while (!(ch>=''&&ch<='')) {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
x=s*f;
}
int New(int val){
a[++tot].val=val;
a[tot].cnt=a[tot].size=;
a[tot].dat=rand();
return tot;
}
void update(int p){
a[p].size=a[a[p].l].size+a[p].cnt+a[a[p].r].size;
}
void build()
{
New(-inf);New(inf);
root=;a[].r=;
update(root);
}
int get_rank_by_val(int p,int val)
{
if (!p) return ;
if (val==a[p].val) return a[a[p].l].size+;
if (val<a[p].val) return get_rank_by_val(a[p].l,val);
return a[a[p].l].size+a[p].cnt+get_rank_by_val(a[p].r,val);
}
int get_val_by_rank(int p,int rank)
{
if (!p) return inf;
if (a[a[p].l].size>=rank) return get_val_by_rank(a[p].l,rank);
if (a[a[p].l].size+a[p].cnt>=rank) return a[p].val;
return get_val_by_rank(a[p].r,rank-a[a[p].l].size-a[p].cnt);
}
void zig(int &p)
{
int q=a[p].l;
a[p].l=a[q].r;a[q].r=p;p=q;
update(a[p].r);update(p);
}
void zag(int &p)
{
int q=a[p].r;
a[p].r=a[q].l;a[q].l=p;p=q;
update(a[p].l);update(p);
}
void insert(int &p,int val)
{
if (!p){
p=New(val);
return;
}
if (val==a[p].val){
a[p].cnt++;update(p);
return;
}
if (val<a[p].val){
insert(a[p].l,val);
if (a[p].dat<a[a[p].l].dat) zig(p);
}
else {
insert(a[p].r,val);
if (a[p].dat<a[a[p].r].dat) zag(p);
}
update(p);
}
int getpre(int val)
{
int ans=;
int p=root;
while (p){
if (val==a[p].val){
if (a[p].l>) {
p=a[p].l;
while (a[p].r>) p=a[p].r;
ans=p;
}
break;
}
if (a[p].val<val&&a[p].val>a[ans].val) ans=p;
if (val<a[p].val) p=a[p].l;else p=a[p].r;
}
return a[ans].val;
}
int getnext(int val)
{
int ans=;
int p=root;
while (p){
if (val==a[p].val){
if (a[p].r>){
p=a[p].r;
while (a[p].l>) p=a[p].l;
ans=p;
}
break;
}
if (a[p].val>val&&a[p].val<a[ans].val) ans=p;
if (val<a[p].val) p=a[p].l;else p=a[p].r;
}
return a[ans].val;
}
void remove(int &p,int val)
{
if (!p) return;
if (val==a[p].val){
if (a[p].cnt>) {
a[p].cnt--;update(p);
return;
}
if (a[p].l||a[p].r){
if (a[p].r==||a[a[p].l].dat>a[a[p].r].dat){
zig(p);remove(a[p].r,val);
}
else {
zag(p);remove(a[p].l,val);
}
update(p);
}
else p=;
return;
}
if (val<a[p].val) remove(a[p].l,val);else remove(a[p].r,val);
update(p);
}
int main()
{
int opt;
build();
read(n);
while (n--)
{
read(opt);int x;
read(x);
if (opt==) insert(root,x);
if (opt==) remove(root,x);
if (opt==) printf("%d\n",get_rank_by_val(root,x)-);
if (opt==) printf("%d\n",get_val_by_rank(root,x+));
if (opt==) printf("%d\n",getpre(x));
if (opt==) printf("%d\n",getnext(x));
}
return ;
}

其实还有vector写法:

#include<bits/stdc++.h>
using namespace std; int n,opt,x;
vector <int> p;
int main()
{
p.reserve(+);
scanf("%d",&n);
while (n--)
{
scanf("%d%d",&opt,&x);
if (opt==) p.insert(lower_bound(p.begin(),p.end(),x),x);
if (opt==) p.erase(lower_bound(p.begin(),p.end(),x));
if (opt==) printf("%d\n",lower_bound(p.begin(),p.end(),x)-p.begin()+);
if (opt==) printf("%d\n",p[x-]);
if (opt==)printf("%d\n",p[lower_bound(p.begin(),p.end(),x)-p.begin()-]);
if (opt==) printf("%d\n",p[upper_bound(p.begin(),p.end(),x)-p.begin()]);
}
return ;
}

数据结构之fhq-treap的更多相关文章

  1. 【数据结构】FHQ Treap详解

    FHQ Treap是什么? FHQ Treap,又名无旋Treap,是一种不需要旋转的平衡树,是范浩强基于Treap发明的.FHQ Treap具有代码短,易理解,速度快的优点.(当然跟红黑树比一下就是 ...

  2. FHQ Treap小结(神级数据结构!)

    首先说一下, 这个东西可以搞一切bst,treap,splay所能搞的东西 pre 今天心血来潮, 想搞一搞平衡树, 先百度了一下平衡树,发现正宗的平衡树写法应该是在二叉查找树的基础上加什么左左左右右 ...

  3. fhq treap——简单又好写的数据结构

    今天上午学了一下fhq treap感觉真的很好用啊qwq 变量名解释: \(size[i]\)表示以该节点为根的子树大小 \(fix[i]\)表示随机权值 \(val[i]\)表示该节点的值 \(ch ...

  4. 【数据结构】平衡树splay和fhq—treap

    1.BST二叉搜索树 顾名思义,它是一棵二叉树. 它满足一个性质:每一个节点的权值大于它的左儿子,小于它的右儿子. 当然不只上面那两种树的结构. 那么根据性质,可以得到该节点左子树里的所有值都比它小, ...

  5. 【POJ2761】【fhq treap】A Simple Problem with Integers

    Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...

  6. 在平衡树的海洋中畅游(四)——FHQ Treap

    Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...

  7. 可持久化treap(FHQ treap)

    FHQ treap 的整理 treap = tree + heap,即同时满足二叉搜索树和堆的性质. 为了使树尽可能的保证两边的大小平衡,所以有一个key值,使他满足堆得性质,来维护树的平衡,key值 ...

  8. 并不对劲的fhq treap

    听说很对劲的太刀流不止会splay一种平衡树,并不对劲的片手流为了反驳他,并与之针锋相对,决定学学高端操作. 很对劲的太刀流-> 据说splay常数极大,但是由于只知道splay一种平衡树能对序 ...

  9. FHQ treap学习(复习)笔记

    .....好吧....最后一篇学习笔记的flag它倒了..... 好吧,这篇笔记也鸽了好久好久了... 比赛前刷模板,才想着还是补个坑吧... FHQ,这个神仙(范浩强大佬),发明了这个神仙的数据结构 ...

  10. 简析平衡树(四)——FHQ Treap

    前言 好久没码过平衡树了! 这次在闪指导的指导下学会了\(FHQ\ Treap\),一方面是因为听说它可以可持久化,另一方面则是因为听说它是真的好写. 简介 \(FHQ\ Treap\),又称作非旋\ ...

随机推荐

  1. 一个通用Makefile的编写

    作者:杨老师,华清远见嵌入式学院讲师. 我们在Linux环境下开发程序,少不了要自己编写Makefile,一个稍微大一些的工程下面都会包含很多.c的源文件.如果我们用gcc去一个一个编译每一个源文件的 ...

  2. Shuttle ESB实现消息推送

    ESB全称Enterprise Service Bus,即企业服务总线.它是传统中间件技术与XML.Web服务等技术结合的产物. ESB的出现改变了传统的软件架构,能够提供比传统中间件产品更为便宜的解 ...

  3. cocos2d_x_03_经常使用类的使用_事件_画图

    一.TextFieldTTF输入框的使用 #pragma mark - 自己定义方法 // 自己定义方法,加入一个 TextField void TextFieldScene::addOneTextF ...

  4. HDU5411CRB and Puzzle(矩阵高速幂)

    题目链接:传送门 题意: 一个图有n个顶点.已知邻接矩阵.问点能够反复用长度小于m的路径有多少. 分析: 首先我们知道了邻接矩阵A.那么A^k代表的就是长度为k的路径有多少个. 那么结果就是A^0+A ...

  5. google 搜索不跳中间页

    Array.prototype.slice.call(document.links,0).forEach(function(link){link.onmousedown = null}) 插件总失效 ...

  6. (转载)Android滑动冲突的完美解决

    Android滑动冲突的完美解决 作者:softwindy_brother 字体:[增加 减小] 类型:转载 时间:2017-01-24我要评论 这篇文章主要为大家详细介绍了Android滑动冲突的完 ...

  7. POJ 3723 Conscription (Kruskal并查集求最小生成树)

    Conscription Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14661   Accepted: 5102 Des ...

  8. 51nod 1770 数数字 找规律,注意进位,时间复杂度O(n)

    题目: 这题很简单,找规律即可. 考虑两次进位: 1.a*b时的进位. 2.aa*b时加法时进位. 代码: #include <bits\stdc++.h> using namespace ...

  9. Java数据库连接——jdbc-odbc桥连接方式及汉字乱码问题

    jdbc-odbc桥连接方式操作数据库SU(Course),其中Course属性有Cno,Cname,Cpno,Ccredit. 步骤: 1.配置数据源 控制面板下搜索管理工具->ODBC数据源 ...

  10. mybastis_20190323

    1 数据表 items.user.orders.orderdetail user id,username,birthday,sex,address; 使用原生态的jdbc的问题总结? 1 数据库链接问 ...