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工作流引擎ccflow新增支持PostgreSQL数据库的功能的发布说明
关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 各位驰骋工作流引擎爱好着,经过驰骋公司与正元公司的共同努力,ccflow支持Post ...
- Android与js互相调用
有话要说: 本篇主要总结了简单的Android与js互相调用的方法. 在开发过程中遇到了需要在安卓中调用js方法的需求,于是将具体的实现过程总结成这篇博客. 效果: 其中“调用安卓方法”按钮是html ...
- Linux下LANMP集成环境中编译增加pdo_odbc模块
linux版本为CentOs6.5,php集成环境为lanmp_v3.1,集成环境中默认的pdo扩展为:mysql, sqlite, sqlite2,现在有需求想链接微软的Access数据库,所以需要 ...
- 转:Git Submodule管理项目子模块
使用场景 当项目越来越庞大之后,不可避免的要拆分成多个子模块,我们希望各个子模块有独立的版本管理,并且由专门的人去维护,这时候我们就要用到git的submodule功能. 常用命令 git clone ...
- 【Oracle RAC】Linux系统Oracle12c RAC安装配置详细记录过程V2.0(图文并茂)
[Oracle RAC]Linux系统Oracle12c RAC安装配置详细过程V2.0(图文并茂) 2 Oracle12c RAC数据库安装准备工作2.1 安装环境介绍2.2 数据库安装软件下载3 ...
- c# 多线程 --Mutex(互斥锁) 【转】
互斥锁(Mutex) 互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它. 互斥锁可适用于一个共享资源每次只能被一个线程访问的情况 函数: //创建一个处于未获取状态的互斥锁 Pub ...
- Cannot read property 'validate' of undefined
在使用element-UI表单验证中一直报错,'Error in event handler for “click”: “TypeError: Cannot read property ‘valida ...
- c# 创建excel表头及内容
主要通过ajax调用函数Getinfo 1.定义表dh DataTable dh = new DataTable(); 2.创建表头 public void CreateCol(string Colu ...
- WIn10系统软件默认安装c盘后消失看不见问题
一.win10系统下c盘,program 文件下 软件一般为32 或者 64位,但是现在win10系统有些C盘会显示program x86 向这种情况的话我们的软件默认安装在这个盘的话可能会造成很多 ...
- Java基础系列--08_集合1
---恢复内容开始--- 集合当中有很多都是应用到泛型的技术,所以在讲集合之前,应该先将泛型的概念普及一下. 泛型: (1)泛型是一种类型,但是这种类型是在编译或者调用方法时才确定. (2 ...