听说很对劲的太刀流不止会splay一种平衡树,并不对劲的片手流为了反驳他,并与之针锋相对,决定学学高端操作。

很对劲的太刀流->

据说splay常数极大,但是由于只知道splay一种平衡树能对序列进行操作,或者进行分裂合并,还不能不写它。

那么常数略小的treap能否对序列操作或者分裂合并呢?想必是能的。

这就是fhq大神的可分裂与合并的treap(据说国家队人手一个自创算法?那必须的不然ctsc论文答辩怎么过)。

它并没有比treap多出什么,反而省了好多事。核心操作只有split和merge。假设每个点有两个数key和w,平衡树维护的是key的性质,小根堆维护的是w的性质。

split(A,k):将A子树中排名不超过k的分一堆,排名超过k的分一堆。要返回两个值,分别是这两个平衡树的根。因为可能有多次祖父、父、子不共线的情况。

void spl(int u,int &l,int &r,int k)
{//称1st-kth为"前一部分",剩下的为"后一部分"
if(!k) l=0,r=u;//排名为0,显然所有数都在后一部分
else if(k==siz[u]) l=u,r=0;//排名与子树大小相等,显然所有数都在前一部分
else if(k<=siz[ls]) r=u,spl(ls,l,ls,k),up(u);//排名小于左子树大小说明右子树全是后一部分,那么还需要划分的就是前一部分
else l=u,spl(rs,rs,r,k-siz[ls]-1), up(u);//和上一行相反
}//会发现它们都是对称的

merge(A,B):类似可并堆。将A与B合并时(假设B中所有key都比A中所有key大),将w更大的合并到w更小的子树上。在这里为了满足二叉搜索树的性质,B合并到A上时要并道A的右儿子(key值都比A点大),A合并到B上时则反之。

void mrg(int &u,int l,int r)
{// l中所有数都小于r中所有数
if(!l || !r)u=l+r;//类似可并堆,有一边是空的就可以直接接过去了
else if(w[l]<w[r])u=l,mrg(rs,rs,r),up(u);//这是小根堆,所以是让w更小的当根
else u=r,mrg(ls,l,ls),up(u);//可并堆是和尽量和更小的儿子合并,但是还要满足平衡树的性质,所以l只能和r的左儿子,r只能和l的右儿子合并
}

其它操作都可以用这两个拼起来,比如insert就是在要插入的位置split,再将插入的数分别与左、右两边合并;delete就是在要删除的位置前后split,再将左右两边merge,中间就不要了。

但是取k数排名(rank)和第k大的数(kth)和splay不大一样。

rank:splay中,这个操作很方便,直接把k转到根就行了。而fhq treap中,不知道排名无法split,只能从上往下找了。

int rank(int u, int k)
{//求出的是小于k的数共有多少个 ,和splay里没什么区别
if(!u) return 0;
if(key[u]>=k) return rank(ls,k);//要注意可能会有一些点的权值相等,如果它们的权值等于k就不大妙了
return rank(rs,k)+siz[ls]+1;//这个函数求严格小于k的数有多少个的,所以比k大的数和等于k的数的处理方式是相同的
}

kth:splay中,取第k大数比较麻烦,因为只能将某个值的数转到根。但是fhq treap中就比较方便,将排名不超过k的split出来后,在split出排名不超过k-1的。剩下的就是排名为k的。

inline int kth(int k)
{
int x,y,z,ans;
spl(rt,x,y,k),spl(x,z,x,k-1),ans=key[x],mrg(x,z,x),mrg(rt,x,y);
return ans;//split之后记得还原
}

普通平衡树的muti-set听上去很烦,但是fhq treap可以有多个相同的点。

这样听上去前驱后继都会变得难求。想必是可以通过巧妙使用split解决的。

prefix:看注释吧

inline int pre(int k)
{
int x,y,z,ans,rk=rank(rt,k);//第一次split后,x中的是所有小于k的;第二次split刚好分出所有小于x的中最大的
spl(rt,x,y,rk),spl(x,z,x,rk-1),ans=key[x],mrg(x,z,x),mrg(rt,x,y);
return ans;
}

suffix:看注释吧

inline int suc(int k)
{
int x,y,z,ans,rk=rank(rt,k+1);//第一次split后,x中是所有小于k+1(小于等于k)的和一个大于等于k+1的;
spl(rt,x,y,rk+1),spl(x,z,x,rk),ans=key[x],mrg(x,z,x),mrg(rt,x,y);//第二次split刚好把那个最大的分出来
return ans;//前驱和后继挺对称的
}

令人不大愉快的是,其实无旋treap的常数和splay差不多。

令人愉快的是,由于没有旋转操作,它是可以可持久化的(不知道比替罪羊树高到哪里去了),而且不用记录父亲。

 #include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dwn(i,x,y) for(register int i=(x);i>=(y);i--)
#define ls son[u][0]
#define rs son[u][1]
#define maxn 100010
#define inf 2147483647
#define s0 siz[0]=0
using namespace std;
int n,m,x,y,z,rt,cnt,tmp,opt;
int w[maxn],key[maxn],siz[maxn],son[maxn][];
inline int read()
{
int x=,f=;
char ch=getchar();
while(isdigit(ch)== && ch!='-')ch=getchar();
if(ch=='-')f=-,ch=getchar();
while(isdigit(ch))x=x*+ch-'',ch=getchar();
return x*f;
}
inline void write(int x)
{
int f=;char ch[];
if(!x){puts("");return;}
if(x<){putchar('-');x=-x;}
while(x)ch[++f]=x%+'',x/=;
while(f)putchar(ch[f--]);
putchar('\n');
}
inline void res(int &u, int k){w[u=++cnt]=rand()<<|rand(),key[u]=k,siz[u]=;}
inline void up(int u) {s0,siz[u]=siz[ls]+siz[rs]+,s0;}
void mrg(int &u,int l,int r)
{// l中所有数都小于r中所有数
if(!l || !r)u=l+r;//类似可并堆,有一边是空的就可以直接接过去了
else if(w[l]<w[r])u=l,mrg(rs,rs,r),up(u);//这是小根堆,所以是让w更小的当根
else u=r,mrg(ls,l,ls),up(u);//可并堆是和尽量和更小的儿子合并,但是还要满足平衡树的性质,所以l只能和r的左儿子,r只能和l的右儿子合并
}
void spl(int u,int &l,int &r,int k)
{//称1st-kth为"前一部分",剩下的为"后一部分"
if(!k) l=,r=u;//排名为0,显然所有数都在后一部分
else if(k==siz[u]) l=u,r=;//排名与子树大小相等,显然所有数都在前一部分
else if(k<=siz[ls]) r=u,spl(ls,l,ls,k),up(u);//排名小于左子树大小说明右子树全是后一部分,那么还需要划分的就是前一部分
else l=u,spl(rs,rs,r,k-siz[ls]-), up(u);//和上一行相反
}//会发现它们都是对称的
int rank(int u, int k)
{//求出的是小于k的数共有多少个 ,和splay里没什么区别
if(!u) return ;
if(key[u]>=k) return rank(ls,k);//要注意可能会有一些点的权值相等,如果它们的权值等于k就不大妙了
return rank(rs,k)+siz[ls]+;//这个函数求严格小于k的数有多少个的,所以比k大的数和等于k的数的处理方式是相同的
}
inline void insert(int k)
{
int x,y,rk=rank(rt,k);//x中的数是严格小于k的,y中的数是大于等于k的
spl(rt,x,y,rk),res(tmp,k),mrg(x,x,tmp),mrg(rt,x,y);
}
inline void del(int k)
{
int x,y,z,rk=rank(rt,k)+;//x中只有一个k,其余都是严格小于k的
spl(rt,x,y,rk),spl(x,x,z,rk-),mrg(rt,x,y);
}
inline int kth(int k)
{
int x,y,z,ans;
spl(rt,x,y,k),spl(x,z,x,k-),ans=key[x],mrg(x,z,x),mrg(rt,x,y);
return ans;//split之后记得还原
}
inline int pre(int k)
{
int x,y,z,ans,rk=rank(rt,k);//第一次split后,x中的是所有小于k的;第二次split刚好分出所有小于x的中最大的
spl(rt,x,y,rk),spl(x,z,x,rk-),ans=key[x],mrg(x,z,x),mrg(rt,x,y);
return ans;
}
inline int suc(int k)
{
int x,y,z,ans,rk=rank(rt,k+);//第一次split后,x中是所有小于k+1(小于等于k)的和一个大于等于k+1的;
spl(rt,x,y,rk+),spl(x,z,x,rk),ans=key[x],mrg(x,z,x),mrg(rt,x,y);//第二次split刚好把那个最大的分出来
return ans;//前驱和后继挺对称的
}
int main()
{
srand();//据说这样能让人获得永生
n=read();w[]=key[]=inf;
rep(i,,n)
{
opt=read(),x=read();
if(opt==)insert(x);
if(opt==)del(x);
if(opt==)write(rank(rt, x)+);
if(opt==)write(kth(x));
if(opt==)write(pre(x));
if(opt==)write(suc(x));
}
}

其实有一种神奇的数据结构能够在O(1)的时间内完成所有操作,只不过不支持查询操作。它叫【我知道但我不告诉你】。

并不觉得盲目膜时提到国家集训队是对劲的,理同【弓这么用,威力堪比大剑三蓄】。

推荐一波电教G。

并不对劲的fhq treap的更多相关文章

  1. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...

  2. NOI 2002 营业额统计 (splay or fhq treap)

    Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...

  3. 【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 ...

  4. 【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 15112  Solved: 4996[Submit][Statu ...

  5. 「FHQ Treap」学习笔记

    话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...

  6. FHQ Treap摘要

    原理 以随机数维护平衡,使树高期望为logn级别 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 因此可持久化 先介绍变量 ; int n; struct Node { int v ...

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

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

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

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

  9. 浅谈fhq treap

    一.简介 fhq treap 与一般的treap主要有3点不同 1.不用旋转 2.以merge和split为核心操作,通过它们的组合实现平衡树的所有操作 3.可以可持久化 二.核心操作 代码中val表 ...

随机推荐

  1. [转载] Asynchronous ActionScript Execution

    Asynchronous ActionScript Execution Date September 19, 2009 Language ActionScript 3.0 Target Flash P ...

  2. You Are the One (区间DP)

    The TV shows such as You Are the One has been very popular. In order to meet the need of boys who ar ...

  3. Personal Recommendation Using Deep Recurrent Neural Networks in NetEase读书笔记

    一.文章综述 1.研究目的:实现网易考拉电商平台的商品高效实时个性化推荐.缩短用户与目标商品的距离,让用户点击最少的页面就可以得到想要的商品 2.研究背景:基于用户和基于物品的协同过滤(Collabo ...

  4. 用SQLLDR来装载date类型的控制文件

    以前给山东某单位做oracle数据库恢复得时候,恢复出来得数据中包含date类型,当时给客户提供得是sqlldr得方式,因为数据量比较大,用sqlldr装载起来速度比较快,所以采用了这种方式,结果在装 ...

  5. c++之析构函数

    #include<iostream>using namespace std;class A{   public:    A(){cout<<"A constructi ...

  6. Prime Ring Problem---hdu1016(dfs)

    http://acm.hdu.edu.cn/showproblem.php?pid=1016 这就是一道简单的dfs  但是是我自己想起来的   必须要记录一下 #include<stdio.h ...

  7. Windows如何在cmd命令行中查看、修改、删除与添加、设置环境变量

    首先明确一点: 所有的在cmd命令行下对环境变量的修改只对当前窗口有效,不是永久性的修改.也就是说当关闭此cmd命令行窗口后,将不再起作用.永久性修改环境变量的方法有两种:一种是直接修改注册表(此种方 ...

  8. 【TFS 2017 CI/CD系列 - 01】-- Agent篇

    一.环境要求: PowerShell 3.0 或者更高版本 VS2015或者更高版本 二.下载Agent: 用浏览器打开TFS,[Settings]--> [Agent Pools]--> ...

  9. 百亿级企业级 RPC 框架开源了!

    今天给大家介绍给一款性能卓越的 RPC 开源框架,其作者就是我推荐每个 Java 程序员都应该看的<Java 生态核心知识点整理>的原作者张玉龙. 说实话我第一次看到这个资料的时候,就感觉 ...

  10. hdu 3853(数学期望入门)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3853 LOOPS Time Limit: 15000/5000 MS (Java/Others)    ...