Treap与fhq_Treap学习笔记
1.普通Treap
通过左右旋来维护堆的性质
左右旋是不改变中序遍历的
这里有几点要注意一下:
(1).由于写了内存回收,在 newnode 的时候要 memset 一下这个节点,因为可能被用过
(2).在删除时要这么写 : 如果只有小于等于1个儿子就直接删
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std; const int MAXN = 100100, inf = 0x7fffffff; struct Node{
int ch[2];
int val, prio;
int cnt, siz;
// Node(){ch[0] = ch[1] = val = cnt = siz = 0;}
}t[MAXN]; int n;
int root, pool_cur, delpool[MAXN], delcur; void init();
int newnode(int val);
void delnode(int cur);
void pushup(int cur);
void rotate(int &cur, int d);
void Insert(int &cur, int val);
void Remove(int &cur, int val);
int getpre(int val);
int getnxt(int val);
int getrankbyval(int cur, int val);
int getvalbyrank(int cur, int rank); int main() {
srand(time(NULL));
scanf("%d", &n);
int opt, x;
init();
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &opt, &x);
switch(opt) {
case 1:{
Insert(root, x);
break;
}
case 2:{
Remove(root, x);
break;
}
case 3:{
printf("%d\n", getrankbyval(root, x) - 1);
break;
}
case 4:{
printf("%d\n", getvalbyrank(root, x + 1));
break;
}
case 5:{
printf("%d\n", getpre(x));
break;
}
case 6:{
printf("%d\n", getnxt(x));
break;
}
}
}
return 0;
} void init() {
newnode(-inf);
newnode(inf);
root = 1;
t[1].ch[1] = 2;
pushup(root);
return;
} inline void delnode(int cur) {
delpool[++delcur] = cur;
return;
} int newnode(int val) {
int cur = (delcur ? delpool[delcur--] : ++pool_cur);
memset(t + cur, 0, sizeof(Node));
t[cur].siz = t[cur].cnt = 1;
t[cur].prio = rand();
t[cur].val = val;
return cur;
} void pushup(int cur) {
t[cur].siz = t[t[cur].ch[0]].siz + t[t[cur].ch[1]].siz + t[cur].cnt;
return;
} void rotate(int &cur, int d) {
int u = t[cur].ch[d];
t[cur].ch[d] = t[u].ch[d^1];
t[u].ch[d^1] = cur;
t[u].siz = t[cur].siz;
pushup(cur);
cur = u;
return;
} void Insert(int &cur, int val) {
if(cur == 0) {
cur = newnode(val);
return;
}
if(t[cur].val == val) {
++t[cur].cnt;
pushup(cur);
return;
}
int d = t[cur].val < val;
Insert(t[cur].ch[d], val);
pushup(cur);
if(t[t[cur].ch[d]].prio < t[cur].prio) rotate(cur, d);
return;
} void Remove(int &cur, int val) {
if(!cur) return;
if(t[cur].val == val) {
int o = cur;
if(t[cur].cnt > 1) {
--t[cur].cnt;
} else {
if(!t[cur].ch[0]) {
cur = t[cur].ch[1];
delnode(o);
} else if(!t[cur].ch[1]) {
cur = t[cur].ch[0];
delnode(o);
} else {
int d = t[t[cur].ch[0]].prio < t[t[cur].ch[1]].prio;
rotate(cur, d ^ 1);
Remove(t[cur].ch[d], val);
}
}
pushup(cur);
} else {
int d = t[cur].val < val;
Remove(t[cur].ch[d], val);
}
pushup(cur);
return;
} int getpre(int val) {
int ans = 1;
int cur = root;
while(cur) {
if(val == t[cur].val) {
if(t[cur].ch[0] > 0) {
cur = t[cur].ch[0];
while(t[cur].ch[1] > 0) cur = t[cur].ch[1];
ans = cur;
}
break;
}
if(t[cur].val < val and t[cur].val > t[ans].val) ans = cur;
cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
}
return t[ans].val;
} int getnxt(int val) {
int ans = 2;
int cur = root;
while(cur) {
if(val == t[cur].val) {
if(t[cur].ch[1] > 0) {
cur = t[cur].ch[1];
while(t[cur].ch[0] > 0) cur = t[cur].ch[0];
ans = cur;
}
break;
}
if(t[cur].val > val and t[cur].val < t[ans].val) ans = cur;
cur = t[cur].val > val ? t[cur].ch[0] : t[cur].ch[1];
}
return t[ans].val;
} int getrankbyval(int cur, int val) {
if(!cur) return 0;
if(val == t[cur].val) return t[t[cur].ch[0]].siz + 1;
return t[cur].val > val ? getrankbyval(t[cur].ch[0], val) : (getrankbyval(t[cur].ch[1], val) + t[t[cur].ch[0]].siz + t[cur].cnt);
} int getvalbyrank(int cur, int rank) {
if(!cur) return 0;
if(t[t[cur].ch[0]].siz >= rank) return getvalbyrank(t[cur].ch[0], rank);
if(t[t[cur].ch[0]].siz + t[cur].cnt >= rank) return t[cur].val;
return getvalbyrank(t[cur].ch[1], rank - t[t[cur].ch[0]].siz - t[cur].cnt);
}
2.fhq_Treap (非旋 Treap )
通过 Split 和 Merge 操作来维护平衡树
(1) 用 fhq_Treap 实现普通 Treap 支持的操作
Split :
把以 cur 为根的树的前 k 个元素分离开
返回值为分离后的两根
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
进入右子树,则一定选当前节点,将它作为分离后左边那棵树的根
进入左子树,则一定不选当前节点,将它作为分离后的右边那棵树的根
Merge :
像可并堆那样进行合并
这样来满足堆性质
int Merge(int x, int y) {
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
这里写一种比较清奇的 getrank , 返回所有小于 val 的元素个数
很好用的
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
加哨兵总是挂,最后就没加 = =
好像也不用加
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std; const int MAXN = 100001; struct Node{
int ch[2], siz, prio, val;
}t[MAXN];
int n, Root, poolcur;
int delpool[MAXN], delcur; inline void pushup(int cur) {
t[cur].siz = t[lson].siz + t[rson].siz + 1;
return;
}
inline void delnode(int cur) {
delpool[++delcur] = cur;
return;
}
inline int newnode(int val) {
register int cur = (delcur ? delpool[delcur--] : ++poolcur);
memset(t + cur, 0, sizeof(Node));
t[cur].val = val;
t[cur].siz = 1;
t[cur].prio = rand();
return cur;
}
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
int Merge(int x, int y) {
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
int findkth(int k) {
pair<int, int> x = Split(Root, k - 1);
pair<int, int> y = Split(x.second, 1);
int ans = t[y.first].val;
Root = Merge(Merge(x.first, y.first), y.second);
return ans;
}
int getpre(int val) {
register int k = getrnk(Root, val);
return findkth(k);
}
int getnxt(int val) {
register int k = getrnk(Root, val + 1);
return findkth(k + 1);
}
void Insert(int val) {
pair<int, int> x = Split(Root, getrnk(Root, val));
Root = Merge(Merge(x.first, newnode(val)), x.second);
return;
}
void Remove(int val) {
register int k = getrnk(Root, val);
pair<int, int> x = Split(Root, k);
pair<int, int> y = Split(x.second, 1);
delnode(y.first);
Root = Merge(x.first, y.second);
return;
}
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
if(c == '-') f = true;
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + c - 48;
c = getchar();
}
return f ? -x : x;
} int main() {
srand(time(NULL));
memset(t, 0, sizeof(Node));
n = rd();
int opt, x;
while(n--) {
opt = rd(); x = rd();
switch(opt) {
case 1: {
Insert(x);
break;
}
case 2: {
Remove(x);
break;
}
case 3: {
printf("%d\n", getrnk(Root, x) + 1);
break;
}
case 4: {
printf("%d\n", findkth(x));
break;
}
case 5: {
printf("%d\n", getpre(x));
break;
}
case 6: {
printf("%d\n", getnxt(x));
break;
}
}
}
return 0;
}
(2) 用 fhq_Treap 实现 Splay 支持的区间操作
fhq_Treap是支持拼接子树的,所以也能够很好的支持区间操作
要对区间 [ l, r ] 进行操作,就先把前 r 个元素 Split 出来,再将前 l - 1 个元素 Split 出来,这样就得到了区间 [ l, r ] 的一棵Treap
之后进行想要的操作即可
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
#include<ctime>
#define lson t[cur].ch[0]
#define rson t[cur].ch[1]
using namespace std; const int MAXN = 100001; struct Node{
int ch[2], siz, val, prio;
bool rvrs;
Node(){ch[0] = ch[1] = siz = val = 0; rvrs = false;}
}t[MAXN];
int n, m, Root, poolcur; inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
if(c == '-') f = true;
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + c - 48;
c = getchar();
}
return f ? -x : x;
}
inline void pushup(int cur) {
t[cur].siz = t[lson].siz + t[rson].siz + 1;
return;
}
inline void pushdown(int cur) {
if(cur && t[cur].rvrs) {
t[cur].rvrs = false;
swap(lson, rson);
t[lson].rvrs ^= 1;
t[rson].rvrs ^= 1;
}
return;
}
inline int newnode(int val) {
register int cur = ++poolcur;
t[cur].val = val;
t[cur].siz = 1;
t[cur].prio = rand();
t[cur].rvrs = false;
return cur;
}
pair<int, int> Split(int cur, int k) {
if(!cur or !k) return make_pair(0, cur);
pushdown(cur); pair<int, int> res;
if(t[lson].siz >= k) {
res = Split(lson, k);
lson = res.second;
pushup(cur);
res.second = cur;
} else {
res = Split(rson, k - t[lson].siz - 1);
rson = res.first;
pushup(cur);
res.first = cur;
}
return res;
}
int Merge(int x, int y) {
pushdown(x); pushdown(y);
if(!x) return y; if(!y) return x;
if(t[x].prio < t[y].prio) {
t[x].ch[1] = Merge(t[x].ch[1], y);
pushup(x);
return x;
} else {
t[y].ch[0] = Merge(x, t[y].ch[0]);
pushup(y);
return y;
}
}
int getrnk(int cur, int val) {
if(!cur) return 0;
return val <= t[cur].val ? getrnk(lson, val) : (getrnk(rson, val) + t[lson].siz + 1);
}
void Insert(int val) {
pair<int, int> x = Split(Root, getrnk(Root, val));
Root = Merge(Merge(x.first, newnode(val)), x.second);
return;
}
void Reverse(int l, int r) {
pair<int, int> x = Split(Root, r);
pair<int, int> y = Split(x.first, l - 1);
t[y.second].rvrs ^= 1;
Root = Merge(Merge(y.first, y.second), x.second);
return;
}
void Recycle(int cur) {
if(!cur) return;
pushdown(cur);
if(lson) Recycle(lson);
if(t[cur].val > 0 and t[cur].val <= n) printf("%d ", t[cur].val);
if(rson) Recycle(rson);
return;
}
inline void init() {
t[1].val = t[1].siz = 1;
Root = 1; poolcur = 1;
t[1].prio = rand();
return;
} int main() {
srand(time(NULL));
n = rd(); m = rd();
init();
for(int i = 2; i <= n; ++i) Insert(i);
int l, r;
while(m--) {
l = rd(); r = rd();
Reverse(l, r);
}
Recycle(Root);
putchar('\n');
return 0;
}
Update:
在写 bzoj1500 时有些奇怪的疑惑: Split 出来之后再 Merge 回去还是原来的树吗?区间反转的 tag 给当前根打上之后再接回去不会 GG 吗?
显然,它还是原来的树,reverse 之后也并不会 GG . 当原来的 x.first 回到它应该的位置后,x.second 整棵子树靠近 x.first 一侧的 tag 也已经下放至更深一层了
Treap与fhq_Treap学习笔记的更多相关文章
- fhq_treap 学习笔记
前言:昨天写NOIp2017队列,写+调辗转了3h+,不知道怎么的,就点进了一个神仙的链接,便在今日学习了神仙的fhq_treap. 简介:fhq_treap功能强大,支持splay支持的所有操作,代 ...
- [总结] fhq_Treap 学习笔记
无旋版 $Treap$. 只需要两个操作即可达到 $splay$ 的所有功能 1.$split$ 它的主要思想就是把一个 $Treap$ 分成两个. $split$ 操作有两种类型,一种是按照权值分配 ...
- 普通平衡树Treap(含旋转)学习笔记
浅谈普通平衡树Treap 平衡树,Treap=Tree+heap这是一个很形象的东西 我们要维护一棵树,它满足堆的性质和二叉查找树的性质(BST),这样的二叉树我们叫做平衡树 并且平衡树它的结构是接近 ...
- 平衡树学习笔记(2)-------Treap
Treap 上一篇:平衡树学习笔记(1)-------简介 Treap是一个玄学的平衡树 为什么说它玄学呢? 还记得上一节说过每个平衡树都有自己的平衡方式吗? 没错,它平衡的方式是......rand ...
- 左偏树 / 非旋转treap学习笔记
背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...
- 「学习笔记」Treap
「学习笔记」Treap 前言 什么是 Treap ? 二叉搜索树 (Binary Search Tree/Binary Sort Tree/BST) 基础定义 查找元素 插入元素 删除元素 查找后继 ...
- Treap-平衡树学习笔记
平衡树-Treap学习笔记 最近刚学了Treap 发现这种数据结构真的是--妙啊妙啊~~ 咳咳.... 所以发一发博客,也是为了加深蒟蒻自己的理解 顺便帮助一下各位小伙伴们 切入正题 Treap的结构 ...
- [学习笔记]平衡树(Splay)——旋转的灵魂舞蹈家
1.简介 首先要知道什么是二叉查找树. 这是一棵二叉树,每个节点最多有一个左儿子,一个右儿子. 它能支持查找功能. 具体来说,每个儿子有一个权值,保证一个节点的左儿子权值小于这个节点,右儿子权值大于这 ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
随机推荐
- iframe实现局部刷新和回调(转)
今天做项目遇到一个问题.就是提交表单的时候,要在后台验证用户名是否存在和验证码是否正确. 当验证码或者用户名存在的时候.在后台弹窗提示.可页面原本file里面符合要求的值刷新没了.用户体验不好.因为用 ...
- JavaScript 很长很长的JS
var BaiduUsers = [], WechatUsers = []; var User = function(id, name, phone, gender, age, salary) { t ...
- svn path already exists的解决办法
这种问题的一般原因是这个path所指的目录在服务器端是一个空目录,对客户端不可见,客户端如果新建了这个目录,而且向服务器端commit的时候就会报错,服务器端此目录已存在,这个时候就会存在一个问题:就 ...
- Android面试题摘录
本文中面试题全部选自<精通Android>(英文名“Pro android 4”)一书的章后面试题,不过这套面试题与书中内容结合比较紧密,所以选择使用时请谨慎. ####C2:Androi ...
- require.js配置路径的用法和css的引入
前端开发在近一两年发展的非常快,JavaScript作为主流的开发语言得到了前所未有的热捧.大量的前端框架出现了,这些框架都在尝试着解决一 些前端开发中的共性问题,但是实现又不尽相同.通常一般的前端加 ...
- DevOps之一 Gitlab的安装与配置
gitlab的安装 参考治疗:https://www.gitlab.com.cn/installation/#centos-7 http://www.21yunwei.com/archives/435 ...
- ArcticCore重构-问题列表1
基于官方arc-stable-9c57d86f66be,AUTOSAR版本3.1.5 基本问题 Arctic Core中的代码组织有很多有待改进的地方,这里先提出几点: 1. 头文件引用混乱,所有头文 ...
- 重温《STL源码剖析》笔记 第一章
源码之前,了无秘密. --侯杰 经典的书,确实每看一遍都能重新收获一遍: 第一章:STL简介 STL的设计思维:对象的耦合性极低,复用性极高,符合开发封闭原则的程序库. STL的价值:1.带给我们一套 ...
- tomcat的配置使用详细版
摘要: 开发者开发部署web应用时通常使用tomcat服务器,很多初学者只懂得在开发工具上配置,但离开了开发工具,自己手动配置部署,并让一个项目跑起来,你会了吗.小编也遇到过这样的困扰.网上查找的资料 ...
- [ SSH框架 ] Struts2框架学习之四(自定义拦截器)
一.Struts2的拦截器 1.1 拦截器概述 拦截器,在AOP( Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.拦截 ...