本文内容部分转自某大佬博客: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. Android学习之GridView图片布局适配经验

    開始解说这篇博客之前,我想问一下,当布局相似GridView这样的多列布局时,我们该怎么布局,才干更好的去适配呢? 扣张图来展示一下 比如这样的需求,三张图片均分屏幕 实现方法: 1.切图固定,比如是 ...

  2. [Angular] Read Custom HTTP Headers Sent by the Server in Angular

    By default the response body doesn’t contain all the data that might be needed in your app. Your ser ...

  3. POJ 题目2774 Long Long Message(后缀数组,求最长公共子串长度)

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 23696   Accepted: 97 ...

  4. group_concat函数

  5. Oracle 10g RAC (linux) ASM 共享存储的管理详解

    ---------ASM 的管理(共享磁盘的管理)1.以 instance 的方式管理 ASM,启动 database 之前必须先启动 ASM instance,ASM instance 启动后,挂载 ...

  6. SpringBoot(四) Web开发 --- Thymeleaf、JSP

    Spring Boot提供了spring-boot-starter-web为Web开发予以支持,spring-boot-starter-web为我们提供了嵌入的Tomcat以及Spring MVC的依 ...

  7. Android 国际区号注册手机号编码 以及常用城市列表

    附上 国际区号编码:我是定义到arrays.xml里面了 <?xml version="1.0" encoding="utf-8"?> <re ...

  8. STM8S103之GPIO

    如何快速了解GPIO,查看Reference manual中GPIO章节,初步了解到GPIO GPIO输入分为:Floating Input和Input with pull-up GPIO输出分为:O ...

  9. LR编写post请求

    函数列表: web_submit_data(); web_custom_request(); web_get_int_property(); 1.web_submit_data(); 2.web_cu ...

  10. SpringBoot学习笔记(8)-----SpringBoot文件上传

    直接上代码,上传文件的前端页面: <body> <form action="/index/upload" enctype="multipart/form ...