简介

\(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);
}

例题

bzoj3224

/*
* @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;
}

bzoj3223

/*
* @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 小结的更多相关文章

  1. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  2. Python自然语言处理工具小结

    Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...

  3. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  4. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

  5. iOS 之UITextFiled/UITextView小结

    一:编辑被键盘遮挡的问题 参考自:http://blog.csdn.net/windkisshao/article/details/21398521 1.自定方法 ,用于移动视图 -(void)mov ...

  6. K近邻法(KNN)原理小结

    K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...

  7. scikit-learn随机森林调参小结

    在Bagging与随机森林算法原理小结中,我们对随机森林(Random Forest, 以下简称RF)的原理做了总结.本文就从实践的角度对RF做一个总结.重点讲述scikit-learn中RF的调参注 ...

  8. Bagging与随机森林算法原理小结

    在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...

  9. scikit-learn 梯度提升树(GBDT)调参小结

    在梯度提升树(GBDT)原理小结中,我们对GBDT的原理做了总结,本文我们就从scikit-learn里GBDT的类库使用方法作一个总结,主要会关注调参中的一些要点. 1. scikit-learn ...

随机推荐

  1. hive基本操作与应用

    通过hadoop上的hive完成WordCount 启动hadoop Hdfs上创建文件夹 上传文件至hdfs 启动Hive 创建原始文档表 导入文件内容到表docs并查看 用HQL进行词频统计,结果 ...

  2. 微信小程序 canvas导出图片模糊

    //保存到手机相册save:function () { wx.canvasToTempFilePath({ x: , y: , width: , //导出图片的宽 height: , //导出图片的高 ...

  3. 你真的懂JavaScript基础类型吗

    夯实Javascript基础. 基本类型有六种: null,undefined,boolean,number,string,symbol. 基本类型的值是保存在栈内存中的简单数据段 基础类型特性 基础 ...

  4. Django 提交 form 表单(使用sqlite3保存数据)

    优化 提交 form 表单,https://www.cnblogs.com/klvchen/p/10608143.html 创建数据库的字段,在 models.py 中添加 from django.d ...

  5. 【AO笔记】关于创建IFeatureClass中的参考系设置——不能为null也不能为IUnknownCoodinateSystem

    创建一个要素类是很简单的,只需要获取一枚IFeatureWorkspace或者一个IFeatureDataset,然后调用其CreateFeatureClass()即可. 这个CreateFeatur ...

  6. 【设计模式】组合模式 Composite Pattern

    树形结构是软件行业很常见的一种结构,几乎随处可见,  比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承 ...

  7. Android 系统服务的获取与创建

    在Android系统中,有一群很厉害的“家伙”,如果把Android系统比喻成一个大帮派,那么这群“家伙”的地位就像那各个分堂的堂主一样,所有的应用就像是各个小马哥,他们要做什么事情,都要堂主审批,审 ...

  8. (办公)工作中的编码不良习惯Java(不定时更新)

    1.别瞎写,方法里能用封装好的类,就别自己写HashMap. 2.方法名,整的方法名都是啥?退出close,用out. 3.git提交版本,自己写的代码,注释,提交版本的时候,一定要清理掉.每个判断能 ...

  9. gcc/g++ 编译参数

    1, -E(大写),预处理 例子:gcc -E test.cpp -o test.i 预处理,把程序里的#开头的替换掉,比如#include,然后生成test.i 2,-P(大写),去掉预处理生成的杂 ...

  10. 记录display:table的使用

    兼容性:不兼容IE7 1.左右对齐 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...