浅谈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 ...
随机推荐
- fastDFS教程Ⅱ-文件服务器迁移
在实际的项目应用中,由于服务器替换或项目变更难免会存在fastDFS文件服务器迁移的工作.本文重点介绍fastDFS文件系统在不同情况下的文件迁移处理方案. 1.迁移时IP地址不变 通过文件服务器存储 ...
- 前端项目模块化的实践2:使用 Webpack 打包基础设施代码
以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...
- jsp学习之包含——include
http://blog.e23.cn/?uid-1255655-action-viewspace-itemid-134232 1.include: Include 指令: <%@ include ...
- 《linux内核分析》chapter3读书笔记
- 20135316王剑桥Linux内核学习笔记
王剑桥Linux内核学习笔记 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计算机是如何工作的 个人理 ...
- Linux上部署Tomcat+Nginx (JavaWeb项目)
https://blog.csdn.net/wohiusdashi/article/details/81147059
- thinkphp5 速查表
本速查表里的类都是think为命名空间的,实例化时省去了 use.用的时候注意. 本速查表里会有四种方法的调用: 公有方法 $class = new Class(); $class->foo ...
- Qt开发之Hello Qt及学习小技巧
创建第一个Qt程序的简单流程 如果安装了andriod或ios的版本也会显示出来,这里只显示了桌面端的 一个项目文件代码结构如下: 如下图可直接编辑label里的文字内容: 运行结果:(快捷键ctrl ...
- 通过动态包含和Ajax机制抽取Web应用的公共页面
在Java Web应用开发中,经常遇到的一种情况是,许多的页面中都包含着“公共页面”,这部分动态页面的特征是:访问量大,会带来较大的性能压力.功能设计上会动态地改变自身的元素.比如在登录前和登录后所展 ...
- Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
大家在中学就已经学过变量的概念了.例如:我们令 x = 100,则可以推出 x*2 = 200 试试下面这段 Python 代码 import turtle turtle.shape("tu ...