fhq_treap 小结
简介
\(fhq\_treap\)是一种非旋平衡树。在学习这篇文章之前,还是先学习一下普通\(treap\)吧
优点
相比于普通的\(treap\),它可以处理区间操作。
相比于\(splay\),它简洁易懂,代码也较短。
缺点
要比\(splay\)和\(treap\)慢
基础操作
\(fhq\_treap\)最基本的两个操作就是分裂和合并。
分裂
即把一个\(treap\)分为两个。有按照权值分和按照大小分两种方式。
具体方法:
比着代码划拉划拉就知道了(懒)。
按权值分
void split(int rt,int val,int &x,int &y) {
if(!rt) {
x = y = 0;
return;
}
if(TR[rt].w <= val) {
x = rt;split(rs,val,rs,y);
}
else {
y = rt;split(ls,val,x,ls);
}
update(rt);
}
按大小分
void split(int rt,int K,int &x,int &y) {
if(!rt) {
x = y = 0;return;
}
down(rt);
if(K <= TR[ls].siz) {
y = rt;split(ls,K,x,ls);
}
else {
x = rt;split(rs,K - TR[ls].siz - 1,rs,y);
}
update(rt);
}
合并
即把两个\(treap\)合并为一个。
注意要让id形成一个堆,且合并的两个树满足其中一个中的权值全部小于另一个。
具体方法:
比较简单,参考代码吧。。
int merge(int x,int y) {
if(!x || !y) return x + y;
if(TR[x].id < TR[y].id) {
TR[x].son[1] = merge(TR[x].son[1],y);
update(x);
return x;
}
TR[y].son[0] = merge(x,TR[y].son[0]);
update(y);
return y;
}
其他操作
插入
插入一个权值为\(x\)的数。
只需要将原来的\(treap\)按权值\(x\)分为\(L,R\)两棵树。
然后把\(x\)节点当成一个\(treap\)与\(L\)合并起来。然后再整体和\(R\)合并起来。
void insert(int x) {
int L,R;
split(root,x,L,R);
root = merge(merge(L,new_node(x)),R);
}
删除
删除一个权值为\(x\)的数。
将原来的\(treap\)按权值\(x\)分为\(L,R\)两棵子树。再按权值\(x-1\)将\(L\)分为\(L,rt\)两棵子树。
这时\(rt\)中就全都是权值为x的点了。删除根节点(也就是将根的两个孩子合并起来)。
操作完成别忘了合并回去。
void del(int x) {
int L,R,rt;
split(root,x,L,R);
split(L,x - 1,L,rt);
rt = merge(ls,rs);
root = merge(merge(L,rt),R);
}
查询排名
查询权值\(x\)的排名(定义为比\(x\)小的数的数量+1)。
将原\(treap\)按权值\(x-1\)分为\(L,R\)两棵子树。\(L\)的大小+1就是答案了。
操作完成别忘了合并回去。
int Rank(int x) {
int L,R;
split(root,x - 1,L,R);
int ret = TR[L].siz + 1;
root = merge(L,R);
return ret;
}
查询第k大
查询排名为\(k\)的数字。
如果排名小于等于左子树大小就查询左子树。
如果排名大于左子树大小+1就查询右子树,并且\(k-=\)左子树大小
否则返回当前节点。
int kth(int rt,int x) {
while(1) {
if(x == TR[ls].siz + 1) return rt;
if(x <= TR[ls].siz) rt = ls;
else {
x -= TR[ls].siz + 1;
rt = rs;
}
}
}
前驱
查询比\(x\)小的数中最大的数。
按权值\(x-1\)将原\(treap\)分为\(L,R\)两棵子树。
\(L\)子树中最大的那个就是答案。
操作完成别忘了合并回去。
int pre(int x) {
int L,R;
split(root,x - 1,L,R);
int ret = kth(L,TR[L].siz);
root = merge(L,R);return ret;
}
后继
查询比\(x\)大的数中最小的数。
按权值\(x\)将原\(treap\)分为\(L,R\)两棵子树。
\(R\)子树中最小的就是答案。
操作完成别忘了合并回去。
int nxt(int x) {
int L,R;
split(root,x,L,R);
int ret = kth(R,1);
root = merge(L,R);return ret;
}
处理区间
对区间\(l,r\)进行处理。
先按大小\(r\)将原\(treap\)分为\(L,R\)两棵子树。
再按大小\(l-1\)将\(L\)分为\(L1,L2\)两棵子树。
\(L2\)子树就是要处理的区间了。
操作完成别忘了合并回去。
void reverse(int l,int r) {
int L,R,rt,tmp;
split(root,r + 1,L,R);
split(L,l,tmp,rt);
TR[rt].rev ^= 1;
root = marge(marge(tmp,rt),R);
}
例题
/*
* @Author: wxyww
* @Date: 2019-04-13 08:47:22
* @Last Modified time: 2019-04-13 11:24:25
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
#define ls TR[rt].son[0]
#define rs TR[rt].son[1]
const int N = 100000 + 100;
ll read() {
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
struct node {
int w,id,siz,son[2];
}TR[N];
int root,tot;
int new_node(int val) {
++tot;
TR[tot].w = val,TR[tot].id = rand(),TR[tot].siz = 1;
return tot;
}
void update(int rt) {
TR[rt].siz = TR[ls].siz + TR[rs].siz + 1;
}
int merge(int x,int y) {
if(!x || !y) return x + y;
if(TR[x].id < TR[y].id) {
TR[x].son[1] = merge(TR[x].son[1],y);
update(x);
return x;
}
TR[y].son[0] = merge(x,TR[y].son[0]);
update(y);
return y;
}
void split(int rt,int val,int &x,int &y) {
if(!rt) {
x = y = 0;
return;
}
if(TR[rt].w <= val) {
x = rt;split(rs,val,rs,y);
}
else {
y = rt;split(ls,val,x,ls);
}
update(rt);
}
void insert(int x) {
int L,R;
split(root,x,L,R);
root = merge(merge(L,new_node(x)),R);
}
void del(int x) {
int L,R,rt;
split(root,x,L,R);
split(L,x - 1,L,rt);
rt = merge(ls,rs);
root = merge(merge(L,rt),R);
}
int Rank(int x) {
int L,R;
split(root,x - 1,L,R);
int ret = TR[L].siz + 1;
root = merge(L,R);
return ret;
}
int kth(int rt,int x) {
while(1) {
if(x == TR[ls].siz + 1) return rt;
if(x <= TR[ls].siz) rt = ls;
else {
x -= TR[ls].siz + 1;
rt = rs;
}
}
}
int pre(int x) {
int L,R;
split(root,x - 1,L,R);
int ret = kth(L,TR[L].siz);
root = merge(L,R);return ret;
}
int nxt(int x) {
int L,R;
split(root,x,L,R);
int ret = kth(R,1);
root = merge(L,R);return ret;
}
int main() {
srand(time(0));
int n = read();
while(n--) {
int opt = read(),x = read();
if(opt == 1) insert(x);
else if(opt == 2) del(x);
else if(opt == 3) printf("%d\n",Rank(x));
else if(opt == 4) printf("%d\n",TR[kth(root,x)].w);
else if(opt == 5) printf("%d\n",TR[pre(x)].w);
else printf("%d\n",TR[nxt(x)].w);
}
return 0;
}
/*
* @Author: wxyww
* @Date: 2019-04-13 10:46:59
* @Last Modified time: 2019-04-13 11:24:56
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
#define ls TR[rt].son[0]
#define rs TR[rt].son[1]
const int N = 100000 + 100;
ll read() {
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
struct node {
int w,id,siz,rev,son[2];
}TR[N];
int tot;
int root,n,m;
void update(int rt) {
TR[rt].siz = TR[ls].siz + TR[rs].siz + 1;
}
void down(int rt) {
if(TR[rt].rev) {
TR[ls].rev ^= 1;
TR[rs].rev ^= 1;
swap(ls,rs);
TR[rt].rev ^= 1;
}
}
int new_node(int x) {
++tot;
TR[tot].id = rand();TR[tot].siz = 1;TR[tot].w = x;TR[tot].rev = 0;
return tot;
}
int marge(int x,int y) {
if(!x || !y) return x + y;
down(x),down(y);
if(TR[x].id < TR[y].id) {
TR[x].son[1] = marge(TR[x].son[1],y);
update(x);
return x;
}
else {
TR[y].son[0] = marge(x,TR[y].son[0]);
update(y);
return y;
}
}
void split(int rt,int K,int &x,int &y) {
if(!rt) {
x = y = 0;return;
}
down(rt);
if(K <= TR[ls].siz) {
y = rt;split(ls,K,x,ls);
}
else {
x = rt;split(rs,K - TR[ls].siz - 1,rs,y);
}
update(rt);
}
void reverse(int l,int r) {
int L,R,rt,tmp;
split(root,r + 1,L,R);
split(L,l,tmp,rt);
TR[rt].rev ^= 1;
root = marge(marge(tmp,rt),R);
}
int build(int l,int r) {
if(l > r) return 0;
int mid = (l + r) >> 1;
int rt = new_node(mid - 1);
ls = build(l,mid - 1);
rs = build(mid + 1,r);
update(rt);
return rt;
}
void print(int rt) {
if(!rt) return;
down(rt);
print(ls);
if(TR[rt].w >= 1 && TR[rt].w <= n) printf("%d ",TR[rt].w);
print(rs);
}
int main() {
n = read(),m = read();
root = build(1,n + 2);
while(m--) {
int l = read(),r = read();
reverse(l,r);
}
print(root);
return 0;
}
fhq_treap 小结的更多相关文章
- 从零开始编写自己的C#框架(26)——小结
一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...
- Python自然语言处理工具小结
Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- iOS--->微信支付小结
iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...
- iOS 之UITextFiled/UITextView小结
一:编辑被键盘遮挡的问题 参考自:http://blog.csdn.net/windkisshao/article/details/21398521 1.自定方法 ,用于移动视图 -(void)mov ...
- K近邻法(KNN)原理小结
K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...
- scikit-learn随机森林调参小结
在Bagging与随机森林算法原理小结中,我们对随机森林(Random Forest, 以下简称RF)的原理做了总结.本文就从实践的角度对RF做一个总结.重点讲述scikit-learn中RF的调参注 ...
- Bagging与随机森林算法原理小结
在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...
- scikit-learn 梯度提升树(GBDT)调参小结
在梯度提升树(GBDT)原理小结中,我们对GBDT的原理做了总结,本文我们就从scikit-learn里GBDT的类库使用方法作一个总结,主要会关注调参中的一些要点. 1. scikit-learn ...
随机推荐
- .net面向对象设计原则
稳定的框架来源于好的设计,好的设计才能出好的作品,掌握面向对象基本原则才会使我们的设计灵活.合理.不僵化,今天就来谈一谈我们.net 面向对象设计的基本原则. 对于一个没有任何设计经验的开发者来说,如 ...
- 个人博客制作如何选择前端模板 thinkcmf后台加载新模板 CSS js文件
我们的博客后台已经搭建好了,接下来我就要选择一个合适的模板做自己的博客,首先要定位你的博客是做什么用的,是属于什么行业,根据自己博客的定位选择适合的模板. 如果你是设计师,又会前端设计开发,那就可以自 ...
- wordpress的excerpt()函数
问题:在wordpres中的single页面,本身引用的<?php the_excerpt(); ?>,但是在页面上显示的却是文章的内容 原因:the_excerpt(); 在excerp ...
- Dynamics 365-关于Solution的那些事(三)
这一篇的内容,是关于Solution的使用建议的,如果大家有什么实用的建议,欢迎留言讨论. 一. 版本控制 Solution是有版本号的,率性的人可能在新建一个solution的时候,直接赋值1.0, ...
- Debain/Ubuntu/Deepin 下使用 ss
如果你有一台 ss 的服务器,在 Debian Like 的环境下要如何***呢? 安装 ss 客户端 如果还没安装 pip 就得先安装 sudo apt-get install python-pip ...
- hbase 工作原理
一.HBASE介绍HBase是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建大规模结构化的存储集群.HBase的目标是存储并处理大型数据,具体 ...
- Python基础之函数和模块
函数的基本使用 函数的定义:把具有独立功能的代码块组织成一个小模块,在需要的时候调用.或者说,函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数的使用:1.定义函数:2.调用函数 ...
- keil 中报错和警告提示解决办法
1.warning: #1-D: last line of file ends without a newline 解决办法:在文件最后一行加入一个回车. 2.error: #134: expecte ...
- 是时候选择一款富文本编辑器了(wangEditor)
需要一款富文本编辑器,当然不能自己造轮子.本来想使用cnblog也在用的TinyMCE,名气大,功能全.但是发现TinyMCE从4.0开始,不再支持直接下载.所以还是决定选用wangEditor.遗憾 ...
- 【Java】+SOFA
https://www.jianshu.com/p/e3dca8d5e9ee sofa脑图