(转)关于treap的板子理解
关于treap的板子理解:
关于结构体的定义:(一般平衡树无法理解的变量名):
v:节点的值;
size:子节点的个数(包括自己);
cnt:相同的值的副本数;
l:左儿子;
r:右儿子;
右旋:
父亲变成左儿子,左儿子变成父亲的右儿子;
void zig(int x)
{
int h=s[x].l;
s[x].l=s[x].r;s[h].r=x;
s[h].size=s[x].size;
up(x);
x=h;
return ;
}
左旋:
就是右旋相反就变成左旋;
void zag(int x)
{
int h=s[x].r;
s[x].r=s[x].l;s[h].l=x;
s[h].size=s[x].size;
up(x);
x=h;
}
插入:
1.insert函数有两个函数,x和k;
这里k取地址会比较方便;
k就是目前节点,开始时就是根节点rt;
2.如果目前节点为0,即为上一个传过来的是0,那么这个值之前没有过,所以新建一个节点,就是
s[k].size=s[k].cnt=;
s[k].ran=rand();
s[k].v=x;
return ;
3.如果这个值之前出现过,那么副本cnt++;
众所周知s[k].l总是小于k,s[k].r总是大于k;
所以见代码
void insert(int x,int &k)
{
if(!k)
{
s[k].size=s[k].cnt=;
s[k].ran=rand();
s[k].v=x;
return ;
}
s[k].size++;
if(s[k].v==x) s[k].cnt++;
if(x>s[k].v)
{
insert(x,s[k].r);
if(s[s[k].l].ran<s[k].ran)zig(k);
}
else if(x<s[k].v)
{
insert(x,s[k].l);
if(s[s[k].l].ran<s[k].ran)zag(k);
}
return ;
}
删除节点:
1.如果就没有这个节点,就直接return;
2.如果这个节点就是这个值,并且这个点的副本>1, tr[k].cnt--;tr[k].size--;
3.如果左节点和右节点只有一个有值,这个点就是他的那个子节点(删除这个点之后就是他的子节点当这个点);
4.他的左儿子虚拟值(平衡树根据这个虚拟值可以提高效率)小于右儿子的虚拟值,那么右旋
5.else 左旋;
inline void del(int x,int &k)
{
if(!k) return ;
if(tr[k].v==x)
{
if(tr[k].cnt>)
{
tr[k].cnt--;
tr[k].size--;
}
else if(tr[k].l*tr[k].r==) k=tr[k].l+tr[k].r;
else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd) zig(k),del(x,k);
else zag(k),del(x,k);
return ;
}
tr[k].size--;
if(tr[k].v>x) del(x,tr[k].l);
else del(x,tr[k].r);
return ;
}
询问排名:
1.如果k==0,那么就return 0
2.如果这个值就是目前的节点的值,那么返回他的左儿子+1;(注意要加1,左儿子的是小于我的,所以+1,就是我的排名);
3.如果目前节点的值大于要查的值就递归他的左子树;
4.否则递归右子树;
inline int qrnk(int x,int k)
{
if(!k) return ;
if(tr[k].v==x) return tr[tr[k].l].size+;
else if(tr[k].v>x) return qrnk(x,tr[k].l);
else return qrnk(x,tr[k].r)+tr[tr[k].l].size+tr[k].cnt;
}
询问排名是x的值;
1.如果没有这个值,就return 0;
2.如果这个排名小于左子树的子树点的值并且排名小于左子树的子树点加上他的副本数的点;
那么就 return tr[k].v;
3.如果要查的值小于等于左子树的儿子值return qnum(x,tr[k].l);;
4.否则递归右子树;
inline int qnum(int x,int k)
{
if(!k) return ;
if(x>tr[tr[k].l].size&&x<=tr[tr[k].l].size+tr[k].cnt) return tr[k].v;
else if(x<=tr[tr[k].l].size) return qnum(x,tr[k].l);
else return qnum(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r);
}
询问前驱:
1.如果没有就return -inf;
2.如果小于这个点的值递归左子树;
否则返回 max(tr[k].v,qpre(x,tr[k].r));
询问后继:
1.(在这里偷懒一下)就是和询问前驱相反的;
inline int qnxt(int x,int k)
{
if(!k) return inf;
if(x>=tr[k].v) return qnxt(x,tr[k].r);
else return min(tr[k].v,qnxt(x,tr[k].l));
}
平衡树的主要作用就是:
- 插入 x 数;
- 删除 x 数(若有多个相同的数,因只删除一个);
- 查询 x 数的排名(若有多个相同的数,因输出最小的排名);
- 查询排名为 x 的数;
- 求 x 的前趋(前趋定义为小于 x ,且最大的数);
- 求 x 的后继(后继定义为大于 x ,且最小的数)。
有一道题叫普通平衡树;适合刚学平衡树的童鞋;
完整代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
const int inf=0x7fffffff;
int n,rt;
int tot=;
struct node{
int v,cnt,l,r,rnd,size;
}tr[];
inline void up(int k)
{
tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].cnt;
}
inline void zig(int &k)
{
int h=tr[k].l;
tr[k].l=tr[h].r,tr[h].r=k;
tr[h].size=tr[k].size;
up(k);
k=h;
return ;
}
inline void zag(int &k)
{
int h=tr[k].r;
tr[k].r=tr[h].l,tr[h].l=k;
tr[h].size=tr[k].size;
up(k);
k=h;
return ;
}
inline void insert(int x,int &k){
if(!k){
k=++tot;
tr[k].v=x;
tr[k].cnt=tr[k].size=;
tr[k].rnd=rand();
return ;
}
tr[k].size++;
if(x==tr[k].v) tr[k].cnt++;
else if(x<tr[k].v)
{
insert(x,tr[k].l);
if(tr[tr[k].l].rnd<tr[k].rnd) zig(k);
}
else if(x>tr[k].v)
{
insert(x,tr[k].r);
if(tr[tr[k].r].rnd<tr[k].rnd) zag(k);
}
return ;
}
inline void del(int x,int &k)
{
if(!k) return ;
if(tr[k].v==x)
{
if(tr[k].cnt>)
{
tr[k].cnt--;
tr[k].size--;
}
else if(tr[k].l*tr[k].r==) k=tr[k].l+tr[k].r;
else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd) zig(k),del(x,k);
else zag(k),del(x,k);
return ;
}
tr[k].size--;
if(tr[k].v>x) del(x,tr[k].l);
else del(x,tr[k].r);
return ;
}
inline int qrnk(int x,int k)
{
if(!k) return ;
if(tr[k].v==x) return tr[tr[k].l].size+;
else if(tr[k].v>x) return qrnk(x,tr[k].l);
else return qrnk(x,tr[k].r)+tr[tr[k].l].size+tr[k].cnt;
}
inline int qnum(int x,int k)
{
if(!k) return ;
if(x>tr[tr[k].l].size&&x<=tr[tr[k].l].size+tr[k].cnt) return tr[k].v;
else if(x<=tr[tr[k].l].size) return qnum(x,tr[k].l);
else return qnum(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r);
}
inline int qpre(int x,int k)
{
if(!k) return -inf;
if(x<=tr[k].v) return qpre(x,tr[k].l);
else return max(tr[k].v,qpre(x,tr[k].r));
}
inline int qnxt(int x,int k)
{
if(!k) return inf;
if(x>=tr[k].v) return qnxt(x,tr[k].r);
else return min(tr[k].v,qnxt(x,tr[k].l));
}
int main()
{
srand(time());
scanf("%d",&n);
for(int i=;i<=n;i++)
{
int f,x;
scanf("%d%d",&f,&x);
if(f==) insert(x,rt);
if(f==) del(x,rt);
if(f==) printf("%d\n",qrnk(x,rt));
if(f==) printf("%d\n",qnum(x,rt));
if(f==) printf("%d\n",qpre(x,rt));
if(f==) printf("%d\n",qnxt(x,rt));
}
return ;
}
蒟蒻理解,如有错误以其他大佬的为主
作者:lsc
(转)关于treap的板子理解的更多相关文章
- Treap 模板
感觉平衡树也没有以前想的那么玄乎,(其实set超好用的),非旋式Treap挺好理解,和可并堆,二叉搜索树有很大联系 推荐博客:http://memphis.is-programmer.com/post ...
- bzoj 3224: Tyvj 1728 普通平衡树【非旋treap】
就是非旋treap的板子 #include<iostream> #include<cstdio> #include<cstdlib> using namespace ...
- 浅谈无旋treap(fhq_treap)
一.简介 无旋Treap(fhq_treap),是一种不用旋转的treap,其代码复杂度不高,应用范围广(能代替普通treap和splay的所有功能),是一种极其强大的平衡树. 无旋Treap是一个叫 ...
- P3369 【模板】普通平衡树(splay)
P3369 [模板]普通平衡树 就是不用treap splay板子,好好背吧TAT #include<iostream> #include<cstdio> #include&l ...
- P4008 [NOI2003]文本编辑器
思路 FHQ Treap的板子 用FHQ Treap维护中序遍历序列即可 然后数组开够! 代码 #include <cstdio> #include <cstring> #in ...
- [日常] HEOI 2019 退役记
HEOI 2019 退役记 先开坑 坐等AFO 啥时候想起来就更一点(咕咕咕) Day 0 早上打了个LCT, 打完一遍过编译一遍AC...(看来不考这玩意了) 然后进行了一些精神文明建设活动奶了一口 ...
- THUWC2019爆零记
Day -1 现在在机房里,准备敲敲板子什么的. 今天晚上放假诶,要好好睡一下.好好睡是不可能的,这辈子不可能的. Day 0 现在在酒店,\(lwh\)神仙在超越,我打了个\(treap\)的板子就 ...
- THUSC2017 游记
你若安好,便是晴天. Day 0 中午就要出发了,上午浮躁的不行,根本写不下题去. 到了火车站之后发现教练和lyc和ztc在4车靠近5车的那一边,然而我在5车靠近4车的那边,尴尬…… 本来是想着上了火 ...
- 50 days before NOI2017
2017.5.31 今天开了这个博客,打算每天来写点东西,嗯...毕竟要NOI了嘛... 第一天跑到常州里集训,打开题目一看湖南集训题... T1刷一下写完,然后交了然后发现错了...赶紧改过来,大概 ...
随机推荐
- bzoj 2083 Intelligence test —— 思路+vector
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2083 先把所有子序列都存下来,总长度应该有限制,所以用 vector 存: 要做到 O(n) ...
- Laravel中常见的错误与解决方法小结
一.报错: 「Can't swap PDO instance while within transaction」 通过查询 Laravel 源代码,可以确认异常是在 setPdo 方法中抛出的: ? ...
- 任务28:RequestDelegate管道实现思路
任务28:RequestDelegate管道实现思路 管道的实现机制 RequestDelegate是管道的核心.ApplicationBuilder就是接收了很多个RequestDelegae把它拼 ...
- poj 3710 Christmas Game【博弈论+SG】
也就是转换到树形删边游戏,详见 https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html #include<iostream> ...
- poj 2891 Strange Way to Express Integers【扩展中国剩余定理】
扩展中国剩余定理板子 #include<iostream> #include<cstdio> using namespace std; const int N=100005; ...
- hdu 3622 Bomb Game【二分+2-SAT+tarjan】
用read()会挂 二分半径,显然最优的是所有原都用这个最小半径,然后2-SAT把相交的圆建图,跑tarjan判一下可行性即可 #include<iostream> #include< ...
- PAT团体程序设计天梯赛 - 模拟赛
由于本人愚笨,最后一题实在无力AC,于是只有前14题的题解Orz 总的来说,这次模拟赛的题目不算难,前14题基本上一眼就有思路,但是某些题写起来确实不太容易,编码复杂度有点高~ L1-1 N个数求和 ...
- 严格次小生成树(lca + 倍增)
题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得 ...
- K8S学习心得 == kube-controller-manager 报错configmaps "extension-apiserver-authentication" is forbidden: User "kubernetes" cannot get resource "configmaps" in API group ""
当我按照教材设置证书,配置好kube-controller的相关条件后,启动kube-controller-manage组件,却意外报错. 一.基本信息如下: 1. kube-controller-m ...
- 限制属性绑定(__slots__)
正常情况下,当定义了一个class并创建实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性 属性和方法是可以直接定义在class中的,但动态绑定允许在程序运行的过程中动态给class加上属 ...