(转)关于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刷一下写完,然后交了然后发现错了...赶紧改过来,大概 ...
随机推荐
- 【转】cocos2dx 内存管理机制
原文地址: http://www.zaojiahua.com/memory-management.html cocos2dx采用的是在堆上分配内存空间,想想看你在写程序的时候对于cocos2dx中的类 ...
- eclipse恢复界面默认设置
使用eclipse的时候有时候会一不小心把一些界面设置给弄乱,可以恢复默认界面设置 eclipse导航栏window选项卡 找到Perspective->点击Reset Perspective ...
- 【转】IntelliJ IDEA搭建Spring环境
//本来在草稿箱写好了,忘记发就被冲掉了,重新再写一遍. Spring初探 Spring初探 在IntelliJ IDEA中创建Spring项目 一个简单的例子介绍框架的作用 那么什么时候new的对象 ...
- 在word文档中添加上角标和下角标
方法一 摘录:https://jingyan.baidu.com/article/02027811b4d2da1bcc9ce5f7.html 方法二 利用MathType数学公式编辑器 exe下载:h ...
- 022--python 模块介绍和time模块
一.模块的含义 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代 ...
- PTA【复数相乘】
队友在比赛时A掉的.吊吊吊!!! 主要考虑这些情况吧||| 案例: /* 3i i -3 i -1-i 1+i i 1 -1-i -1-i */ -3 -3i -2i i 2i #include &l ...
- poj1477(水)
犯了一个错误,贡献了一次CE: G++里面没有头文件,用scanf会CE:然而C++就可以. 两大cow解释: 最好不要c 的输入和c++的一起用 (特别是关同步的时候) 然而好像他们也不是很了解.. ...
- 一起学Android之Activity
概述 本文以一个简单的小例子,简述Android开发中Activity的相关知识,仅供学习分享使用. 什么是Activity? Activity是一个应用程序组件,通常显示为一个页面,用户可以通过Ac ...
- icomoon字体使用
如何灵活利用免费开源图标字体-IcoMoon篇 by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/w ...
- 根据 目录号 案卷号 用户名 查询 page 中 的条数
select count(*) from am_b_page a join am_b_entry b on a.entry_id=b.entry_id where b.catalogue_code=' ...