浅谈fhq treap
一、简介
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的更多相关文章
- 浅谈BST(二叉查找树)
目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...
- 在平衡树的海洋中畅游(四)——FHQ Treap
Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- fhq treap最终模板
新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
随机推荐
- 使用Python实时获取cmd的输出
最近发现一个问题,一个小伙儿写的console程序不够健壮,监听SOCKET的时候容易崩,造成程序的整体奔溃,无奈他没有找到问题的解决办法,一直解决不了,可是这又是一个监控程序,还是比较要紧的,又必须 ...
- stl源码剖析 详细学习笔记 hashset hashmap
//---------------------------15/03/26---------------------------- //hash_set { /* hash_set概述: 1:这是一个 ...
- ECS centos7 使用外部邮件服务商的465加密端口
ECS centos7 使用外部邮件服务商的465加密端口发送邮件. 1.修改/etc/mail.rc 文件中添加以下的 set smtp="smtps://smtp.163.com:465 ...
- Excel读取Word Table元素
Option Explicit Sub Mian() Application.ScreenUpdating = False Application.DisplayAlerts = False Appl ...
- Unity导入模型出现 (Avatar Rig Configuration mis-match. Bone length in configuration does not match position in animation)?
昨天遇到这两个模型导入的问题,查了一下资料,自己摸索了一下解决方法..总结一下~ 出现的原因:(问题1)Warning 当模型文件导入以后并且设置Animation Type是Generic的时候,动 ...
- 机器学习英雄访谈录之 DL 实践家:Dominic Monn
目录 机器学习英雄访谈录之 DL 实践家:Dominic Monn 正文 对我的启发 机器学习英雄访谈录之 DL 实践家:Dominic Monn Sanyam Bhutani 是 Medium 上一 ...
- commitizen和cz-customizable配置git commit message
起因 团队对提交的commit message格式有约定俗称的要求,但是没有一个统一的规范,导致大家提交的commit message或多或少不太一样.因此,需要一个工具来帮助大家统一commit m ...
- Linux内核分析作业 NO.2
操作系统是如何工作的 于佳心 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...
- Practice1小学四则运算
本次实验是做一个自动生成小学四则运算的小程序,对于我来说是检验基础的一次实验,要运用Visual C++来编写完成,“自动生成”第一印象是要用到Random()函数,“加减乘除”则应该用到switch ...
- nginx转发swoole以及nginx负载
nginx作为静态服务器同时转发swoole配置: location /{root //静态文件目录;index index.html index.htm;//默认首页 if(!-e $request ...