一、简介

fhq treap 与一般的treap主要有3点不同

1、不用旋转

2、以merge和split为核心操作,通过它们的组合实现平衡树的所有操作

3、可以可持久化

二、核心操作

代码中val表示节点权值,pri表示节点的优先级,维护小根堆

1、split

将1个treap分裂为两个treap

分裂主要有两种:以权值k作为分界点、以位置k作为分界点

①以权值k作为分界点

设原来的treap根节点为root,分裂后的<=k的treap A 的根节点为x,>k的treap B 的根节点为y

当前节点指针now从root开始,在treap A 的x位置加点,treap B 的y位置加点

若now的权值<=k,那么以now为根的左子树全部包含在A中,

把now给x,去now的右子树,x变成x的右子树,y还是y

若now的权值>k,那么以now为分的右子树全部包含在B中,

把now给y,去now的左子树,x还是x,y变成y的左子树

void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(val[now]<=k)
{
x=now;
split(ch[now][],k,ch[now][],y);
}
else
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
update(now);
}
}

②以位置k作为分界点

设原来的treap根节点为root,分裂后的前k个节点的treap A 的根节点为x,剩下的节点的treap B 的根节点为y

判断放在哪颗treap的标准改成k和子树大小的比较,原理同上

void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(k<=siz[ch[now][]])
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
else
{
x=now;
split(ch[now][],k-siz[ch[now][]]-,ch[now][],y);
}
update(now);
}
}

2、merge

合并以x为根的treap A 和 以y为根的treap B

前提:A的权值全部<B的权值

注意在merge的过程中,维护好堆和二叉搜索树的性质

若优先级x<y,

维护堆的性质-->y成为x的子节点

维护二叉搜索树的性质-->y在x的右子树

若优先级x>y,

维护堆的性质-->x成为y的子节点

维护二叉搜索树的性质-->x在y的左子树

int merge(int x,int y)
{
if(!x || !y) return x+y;
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
}

三、单点操作

1、建树

建树有两种方式:nlogn 一个一个插、n 直接建

这里主要说后者

每次取mid作为当前子树的根节点即可

int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} int build(int l,int r)
{
if(l>r) return ;
int mid=l+r>>,v=val[mid];
int now=new_node(v);
ch[now][]=build(l,mid-);
ch[now][]=build(mid+,r);
update(now);
return now;
}

2、插入

添加权值为v的点

1、按权值以v为分界点,split成两棵treapA(<=v),B( >v )

2、合并A和v,得到C

3、合并C和B

merge要求x的权值<y决定了 合并的顺序

split(root,v,x,y);
root=merge(merge(x,new_node(v)),y);

3、删除

删除一个权值为v的点

1、按权值以v为分界点,split成两棵treap A(<=v),B( >v)

2、把A按权值以v-1为分界点,split成两棵treap C(<=v-1) ,D(=v)

3、合并D的根节点的左右子树,相当于删除根节点,得到treap E

4、合并C和E,得F

5、合并F和B

split(root,v,x,z);
split(x,v-,x,y);
y=merge(ch[y][],ch[y][]);
root=merge(merge(x,y),z);

4、查询v的排名

1、按权值以v-1为分界点,split成两棵treap A(<=v-1)  B(>=v)

2、A的大小+1 即为权值v的排名

3、合并A、B

split(root,v-,x,y);
cout<<siz[x]+<<'\n';
root=merge(x,y);

5、查询排名为x的数

int get_kth(int now,int k)
{
while()
{
if(k<=siz[ch[now][]]) now=ch[now][];
else
{
k-=siz[ch[now][]];
if(k==) return now;
k--;
now=ch[now][];
}
}
} cout<<val[get_kth(root,v)]<<'\n';

6、查找比v小的最大的数(前驱)

1、按权值以v-1为分界点,split成两棵treap A(<=v-1),B(>=v)

2、在A中查找A的排名最靠后的数,输出

3、合并A和B

split(root,v-,x,y);
cout<<val[get_kth(x,siz[x])]<<'\n';
root=merge(x,y);

7、查询比v大的最小的数(后继)

1、按权值以v为分界点,split成两棵treap A(<=v),B(>v)

2、在B中找最靠前的数,输出

3、合并A和B

split(root,v,x,y);
cout<<val[get_kth(y,)]<<'\n';
root=merge(x,y);

四、区间操作

对区间[l,r]执行某种操作

1、以位置r作为分界点,split成两棵treap A(前r个节点)、B(位置r之后的节点)

2、在A中以位置l-1为分界点,split成两棵treap C(前l-1个节点)、D(第l到第r个节点)

3、对D执行操作

注意因为执行区间操作,增加2个虚拟节点,最前面一个,最后面一个,所有节点后移一位

加虚拟节点的原因是 如果对区间[1,x]或[x,n]操作,那我们需要用到1-1 号 点和n+1号店

split(root,r+,a,b);
split(a,l,c,d);
work(d)

五、模板

1、普通平衡树

https://www.luogu.org/problemnew/show/P3369

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream> using namespace std; #define N 100001 int root; int tot;
int fa[N],ch[N][],val[N],pri[N],siz[N]; void read(int &x)
{
x=; int f=; char c=getchar();
while(!isdigit(c)) { if(c=='-') f=-; c=getchar(); }
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
x*=f;
} void update(int x)
{
siz[x]=siz[ch[x][]]+siz[ch[x][]]+;
} int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} int merge(int x,int y)
{
if(!x || !y) return x+y;
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
} void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(val[now]<=k)
{
x=now;
split(ch[now][],k,ch[now][],y);
}
else
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
update(now);
}
} int get_kth(int now,int k)
{
while()
{
if(k<=siz[ch[now][]]) now=ch[now][];
else
{
k-=siz[ch[now][]];
if(k==) return now;
k--;
now=ch[now][];
}
}
} int main()
{
srand(time()+);
int n,u,v;
int x,y,z;
read(n);
while(n--)
{
read(u); read(v);
if(u==)
{
split(root,v,x,y);
root=merge(merge(x,new_node(v)),y);
}
else if(u==)
{
split(root,v,x,z);
split(x,v-,x,y);
y=merge(ch[y][],ch[y][]);
root=merge(merge(x,y),z);
}
else if(u==)
{
split(root,v-,x,y);
cout<<siz[x]+<<'\n';
root=merge(x,y);
}
else if(u==)
{
cout<<val[get_kth(root,v)]<<'\n';
}
else if(u==)
{
split(root,v-,x,y);
cout<<val[get_kth(x,siz[x])]<<'\n';
root=merge(x,y);
}
else
{
split(root,v,x,y);
cout<<val[get_kth(y,)]<<'\n';
root=merge(x,y);
}
}
}

2、文艺平衡树

https://www.luogu.org/problemnew/show/P3391

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream> using namespace std; #define N 100005 int n; int tot,root; int siz[N],ch[N][],val[N],pri[N];
bool rev[N]; void read(int &x)
{
x=; int f=; char c=getchar();
while(!isdigit(c)) { if(c=='-') f=-; c=getchar(); }
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
x*=f;
} int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} void update(int x)
{
siz[x]=+siz[ch[x][]]+siz[ch[x][]];
} int build(int l,int r)
{
if(l>r) return ;
int mid=l+r>>,v=mid-;
int now=new_node(v);
ch[now][]=build(l,mid-);
ch[now][]=build(mid+,r);
update(now);
return now;
} void down(int x)
{
rev[x]^=;
swap(ch[x][],ch[x][]);
rev[ch[x][]]^=;
rev[ch[x][]]^=;
} int merge(int x,int y)
{
if(!x || !y) return x+y;
if(rev[x]) down(x);
if(rev[y]) down(y);
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
} void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(rev[now]) down(now);
if(k<=siz[ch[now][]])
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
else
{
x=now;
split(ch[now][],k-siz[ch[now][]]-,ch[now][],y);
}
update(now);
}
} void reverse(int l,int r)
{
int a,b,c,d;
split(root,r+,a,b);
split(a,l,c,d);
rev[d]^=;
root=merge(merge(c,d),b);
} void dfs(int x)
{
if(rev[x]) down(x);
if(ch[x][]) dfs(ch[x][]);
if(val[x]>= && val[x]<=n) cout<<val[x]<<' ';
if(ch[x][]) dfs(ch[x][]);
} int main()
{
srand(time()+);
int m;
read(n); read(m);
root=build(,n+);
int l,r;
while(m--)
{
read(l); read(r);
reverse(l,r);
}
dfs(root);
}

浅谈fhq treap的更多相关文章

  1. 浅谈BST(二叉查找树)

    目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...

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

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

  3. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  4. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  5. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  6. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  7. fhq treap最终模板

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

  8. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  9. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

随机推荐

  1. vs2013 std::sort 分析

    由于之前在debug模式下发现stl的sort简直慢到不能忍,所以自己写了一个sgi的sort,后来发现在release模式下,vs自带的sort快的不行,就研究了下. 这里有些和sgi-stl相通的 ...

  2. 浅谈android Service和BroadCastReceiver

    1.题记 Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序. 广播接收者(BroadcastRece ...

  3. 2017qq红包雨最强攻略

    这个只支持苹果手机,而且要有苹果电脑,只有苹果手机是不行的. QQ红包规则:只要你到达指定的位置,就可以领取附近的红包,一般也就几毛,还有几分的,当然也不排除有更高的,只不过我是没遇到... 那么既然 ...

  4. keycode值对照表

    转载自:https://segmentfault.com/a/1190000005828048 字母和数字键的键码值(keyCode) 按键 键码 按键 键码 按键 键码 按键 键码 A 65 J 7 ...

  5. Bloom Filter解析

    布隆过滤器简介:https://www.cnblogs.com/Jack47/p/bloom_filter_intro.html 布隆过滤器详解:原文链接:http://www.cnblogs.com ...

  6. 【Alpha】第三次Scrum meeting

    今日任务一览: 导航栏诞生 前期准备的Latex文本将撰写完毕 生成燃尽图的问题已经解决 姓名 今日完成任务 所耗时间 刘乾 用Github成功生成了燃尽图(真是不容易啊...),与架构师继续每日面基 ...

  7. github优缺点

    以前bitbucket沒有支援git github可以直接在網站上瀏覽push的圖片 github 可以針對code行數直接留言與回覆 github Markdown支援很好 github 的issu ...

  8. 【python】自学笔记

    参考文献 1.环境安装 1.1 python 工作环境 2.7.14 1.2 pycharm community2018.1.1 4 x64 2.第一行代码 2.1 python交互模式, >& ...

  9. win 批处理

    前言 批处理文件(batch file)包含一系列 DOS命令,通常用于自动执行重复性任务.用户只需双击批处理文件便可执行任务,而无需重复输入相同指令.编写批处理文件非常简单,但难点在于确保一切按顺序 ...

  10. Hbase远程连接:Can't get the locations

    当Java API远程连接出错:Can't get the locations 原先填入的是IP地址,后来改为HOSTS文件中配置的主机名问题解决,如下红色字体部分: conf.set("h ...