一、简介

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. fastDFS教程Ⅱ-文件服务器迁移

    在实际的项目应用中,由于服务器替换或项目变更难免会存在fastDFS文件服务器迁移的工作.本文重点介绍fastDFS文件系统在不同情况下的文件迁移处理方案. 1.迁移时IP地址不变 通过文件服务器存储 ...

  2. 前端项目模块化的实践2:使用 Webpack 打包基础设施代码

    以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...

  3. jsp学习之包含——include

    http://blog.e23.cn/?uid-1255655-action-viewspace-itemid-134232 1.include: Include 指令: <%@ include ...

  4. 《linux内核分析》chapter3读书笔记

  5. 20135316王剑桥Linux内核学习笔记

    王剑桥Linux内核学习笔记 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计算机是如何工作的 个人理 ...

  6. Linux上部署Tomcat+Nginx (JavaWeb项目)

    https://blog.csdn.net/wohiusdashi/article/details/81147059

  7. thinkphp5 速查表

    本速查表里的类都是think为命名空间的,实例化时省去了  use.用的时候注意. 本速查表里会有四种方法的调用: 公有方法 $class = new Class();  $class->foo ...

  8. Qt开发之Hello Qt及学习小技巧

    创建第一个Qt程序的简单流程 如果安装了andriod或ios的版本也会显示出来,这里只显示了桌面端的 一个项目文件代码结构如下: 如下图可直接编辑label里的文字内容: 运行结果:(快捷键ctrl ...

  9. 通过动态包含和Ajax机制抽取Web应用的公共页面

    在Java Web应用开发中,经常遇到的一种情况是,许多的页面中都包含着“公共页面”,这部分动态页面的特征是:访问量大,会带来较大的性能压力.功能设计上会动态地改变自身的元素.比如在登录前和登录后所展 ...

  10. Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量

    大家在中学就已经学过变量的概念了.例如:我们令 x = 100,则可以推出 x*2 = 200 试试下面这段 Python 代码 import turtle turtle.shape("tu ...