BZOJ 3224 - 普通平衡树 - [Treap][Splay]
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
HINT
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
题解1:
Treap模板题。
由于普通二叉搜索树容易退化成链状,考虑到在随机数据下产生的BST是趋近平衡树的,因此Treap就是用“随机”来创造平衡条件。
在原来BST的基础上,我们可以对树上所有节点都另外增加一个随机生成的权值 $dat$,迫使整棵BST对于 $dat$ 满足“堆性质”。
在Splay的学习中我们已经知道,右旋zig和左旋zag是不会影响BST的“BST性质”的,而通过zig和zag我们正好又可以达到交换父子节点位置的目的,
因此,我们可以参照二叉堆,如果父子两节点不满足堆性质,则用zig或者zag交换两者位置,从而迫使整棵树满足关于 $dat$ 的“堆性质”。
正是由于这颗二叉树,键值 $key$ 满足BST性质,另一个权值 $dat$ 满足堆性质,因此用单词 tree 和 heap 构成了该数据结构的名称 treap。
AC代码(Treap):
#include<bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
const int maxn=1e5+; /******************************** Treap - st ********************************/
int root,nodecnt;
int ch[maxn][];
int key[maxn],dat[maxn];
int cnt[maxn],siz[maxn];
int NewNode(int val)
{
int x=++nodecnt;
key[x]=val; dat[x]=rand();
cnt[x]=siz[x]=;
ch[x][]=ch[x][]=;
return x;
}
void Pushup(int x)
{
siz[x]=siz[ch[x][]]+siz[ch[x][]]+cnt[x];
}
void Init()
{
root=nodecnt=;
key[]=dat[]=;
cnt[]=siz[]=;
ch[][]=ch[][]=;
}
void Build()
{
Init();
NewNode(-INF);
NewNode(INF);
ch[root=][]=;
Pushup(root);
}
void zig(int &x)
{
int lc=ch[x][];
ch[x][]=ch[lc][], ch[lc][]=x, x=lc;
Pushup(ch[x][]), Pushup(x);
}
void zag(int &x)
{
int rc=ch[x][];
ch[x][]=ch[rc][], ch[rc][]=x, x=rc;
Pushup(ch[x][]), Pushup(x);
}
int GetRank(int x,int val)
{
if(x==) return ;
if(val==key[x]) return siz[ch[x][]]+;
if(val<key[x]) return GetRank(ch[x][],val);
return siz[ch[x][]]+cnt[x]+GetRank(ch[x][],val);
}
int GetKth(int x,int k)
{
if(x==) return INF;
if(siz[ch[x][]]>=k) return GetKth(ch[x][],k);
if(siz[ch[x][]]+cnt[x]>=k) return key[x];
return GetKth(ch[x][],k-siz[ch[x][]]-cnt[x]);
}
void Insert(int &x,int val)
{
if(x==)
{
x=NewNode(val);
return;
}
if(val==key[x])
{
cnt[x]++;
Pushup(x);
return;
}
else if(val<key[x])
{
Insert(ch[x][],val);
if(dat[x] < dat[ch[x][]]) zig(x);
}
else
{
Insert(ch[x][],val);
if(dat[x] < dat[ch[x][]]) zag(x);
}
Pushup(x);
}
void Remove(int &x,int val)
{
if(x==) return;
if(val==key[x])
{
if(cnt[x]>)
{
cnt[x]--;
Pushup(x);
return;
}
if(ch[x][] || ch[x][])
{
if(ch[x][]== || dat[ch[x][]] > dat[ch[x][]]) zig(x), Remove(ch[x][],val);
else zag(x), Remove(ch[x][],val);
Pushup(x);
}
else x=;
return;
}
(val<key[x])?Remove(ch[x][],val):Remove(ch[x][],val);
Pushup(x);
}
int GetPre(int val)
{
int ans=; //a[1].val==-INF
int x=root;
while(x)
{
if(val==key[x])
{
if(ch[x][]>)
{
x=ch[x][];
while(ch[x][]>) x=ch[x][];
ans=x;
}
break;
}
if(key[x]<val && key[x]>key[ans]) ans=x;
x=(val<key[x])?ch[x][]:ch[x][];
}
return key[ans];
} int GetNxt(int val)
{
int ans=; //a[2].val==INF
int x=root;
while(x)
{
if(val==key[x])
{
if(ch[x][]>)
{
x=ch[x][];
while(ch[x][]>) x=ch[x][];
ans=x;
}
break;
}
if(key[x]>val && key[x]<key[ans]) ans=x;
x=(val<key[x])?ch[x][]:ch[x][];
}
return key[ans];
}
/******************************** Treap - ed ********************************/ int main()
{
int n;
cin>>n;
Build();
while(n--)
{
int opt,x;
scanf("%d%d",&opt,&x);
switch(opt)
{
case :
Insert(root,x);
break;
case :
Remove(root,x);
break;
case :
printf("%d\n",GetRank(root,x)-);
break;
case :
printf("%d\n",GetKth(root,x+));
break;
case :
printf("%d\n",GetPre(x));
break;
case :
printf("%d\n",GetNxt(x));
break;
}
}
}
注:本模板的 $zig(x)$ 和 $zag(x)$ 中 $x$,是旋转前处于父亲节点位置。
题解2:
Splay模板题。
Splay的原理以及实现参考:伸展树(Splay Tree)进阶 - 从原理到实现
AC代码(Splay):
#include<bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
const int maxn=1e5+; /******************************** splay - st ********************************/
#define Key_value ch[ch[root][1]][0]
int root,nodecnt;
int par[maxn],ch[maxn][];
int key[maxn],cnt[maxn],siz[maxn];
void NewNode(int &x,int p,int k)
{
x=++nodecnt;
par[x]=p;
ch[x][]=ch[x][]=;
key[x]=k;
cnt[x]=siz[x]=;
}
void Pushup(int x)
{
siz[x]=siz[ch[x][]]+siz[ch[x][]]+cnt[x];
}
void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig
{
int y=par[x];
ch[y][!type]=ch[x][type]; par[ch[x][type]]=y;
if(par[y]) ch[par[y]][(ch[par[y]][]==y)]=x;
par[x]=par[y];
ch[x][type]=y; par[y]=x;
Pushup(y); Pushup(x);
}
void Splay(int x,int goal)
{
while(par[x]!=goal)
{
if(par[par[x]]==goal) Rotate(x,ch[par[x]][]==x); //左孩子zig,右孩子zag
else
{
int y=par[x];
int type=(ch[par[y]][]==y); //type=0,y是右孩子;type=1,y是左孩子
if(ch[y][type]==x)
{
Rotate(x,!type);
Rotate(x,type);
}
else
{
Rotate(y,type);
Rotate(x,type);
}
}
}
if(goal==) root=x;
}
int GetMin(int x)
{
while(ch[x][]) x=ch[x][];
return x;
}
int GetMax(int x)
{
while(ch[x][]) x=ch[x][];
return x;
}
void Init() //初始化,前后各加一个空节点
{
root=nodecnt=;
par[]=ch[][]=ch[][]=;
cnt[]=siz[]=;
key[]=;
NewNode(root,,-INF); //头部加入一个空位
NewNode(ch[root][],root,INF); //尾部加入一个空位
Pushup(ch[root][]);
Pushup(root);
} void Insert(int val)
{
int x=root;
while()
{
if(val==key[x])
{
cnt[x]++;
Pushup(x);
Splay(x,);
break;
}
int type=val>key[x];
if(ch[x][type]==)
{
NewNode(ch[x][type],x,val);
Pushup(x);
Splay(ch[x][type],);
break;
}
else x=ch[x][type];
}
}
void Delete(int val)
{
int x=root;
while(x)
{
if(val==key[x])
{
if(cnt[x]>)
{
cnt[x]--;
Pushup(x);
Splay(x,);
return;
}
if(ch[x][] && ch[x][])
{
Splay(GetMax(ch[x][]),);
Splay(GetMin(ch[x][]),root);
par[Key_value]=; Key_value=;
Pushup(ch[root][]); Pushup(root);
return;
}
int fa=par[x],type=(ch[fa][]==x);
if(ch[x][])
{
par[ch[x][]]=fa;
ch[fa][type]=ch[x][];
Pushup(fa);
Splay(ch[fa][type],);
return;
}
if(ch[x][])
{
par[ch[x][]]=fa;
ch[fa][type]=ch[x][];
Pushup(fa);
Splay(ch[fa][type],);
return;
}
ch[fa][type]=;
Pushup(fa);
Splay(fa,);
return;
}
if(val<key[x]) x=ch[x][];
else x=ch[x][];
}
}
int GetRank_pos;
int GetRank(int x,int val)
{
if(x==) return ;
if(val==key[x]) return siz[ch[GetRank_pos=x][]]+;
if(val<key[x]) return GetRank(ch[x][],val);
return siz[ch[x][]]+cnt[x]+GetRank(ch[x][],val);
}
int GetKth_pos;
int GetKth(int x,int k)
{
if(x==) return ;
if(siz[ch[x][]]>=k) return GetKth(ch[x][],k);
if(siz[ch[x][]]+cnt[x]>=k) return key[GetKth_pos=x];
return GetKth(ch[x][],k-siz[ch[x][]]-cnt[x]);
}
/******************************** splay - ed ********************************/ int main()
{
int n;
cin>>n;
Init();
while(n--)
{
int opt,x;
scanf("%d%d",&opt,&x);
switch(opt)
{
case :
Insert(x);
break;
case :
Delete(x);
break;
case :
printf("%d\n",GetRank(root,x)-);
Splay(GetRank_pos,);
break;
case :
printf("%d\n",GetKth(root,x+));
Splay(GetKth_pos,);
break;
case :
Insert(x);
printf("%d\n",key[GetMax(ch[root][])]);
Delete(x);
break;
case :
Insert(x);
printf("%d\n",key[GetMin(ch[root][])]);
Delete(x);
break;
}
}
}
BZOJ 3224 - 普通平衡树 - [Treap][Splay]的更多相关文章
- Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...
- BZOJ 3224 普通平衡树(Treap模板题)
3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 14301 Solved: 6208 [Submit][ ...
- [洛谷P3369] 普通平衡树 Treap & Splay
这个就是存一下板子...... 题目传送门 Treap的实现应该是比较正经的. 插入删除前驱后继排名什么的都是平衡树的基本操作. #include<cstdio> #include< ...
- BZOJ 3224 普通平衡树
这个是第一份完整的treap代码.嗯...虽然抄的百度的版,但还是不错的. !bzoj上不能用srand. #include<iostream>#include<cstdio> ...
- Luogu 3369 / BZOJ 3224 - 普通平衡树 - [替罪羊树]
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...
- BZOJ 3224 普通平衡树 | 平衡树模板
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> # ...
- bzoj 3224 普通平衡树 vactor的妙用
3224: Tyvj 1728 普通平衡树 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/ ...
- [bzoj 3224]手写treap
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3224 bzoj不能用time(0),看到这个博客才知道,我也RE了好几发…… #inclu ...
- BZOJ 3224 普通平衡树(树状数组)
题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=3224 题意:维护以下操作:(1)插入x:(2)删除x(若有多个相同的数,只删除一个)(3 ...
随机推荐
- Effective Java 第三版——47. 优先使用Collection而不是Stream来作为方法的返回类型
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Android 四大组件 Service 服务
1.Service简单介绍 依照使用范围分类: 类别 优点 缺点 差别 应用 本地服务 Local Service 本地服务在一定程度上节约了资源,另外本地服务由于是在同一进程,因此不须要IPC,也 ...
- Atitit 错误处理机制:(1)静默模式(2)警告模式 (3)异常模式
Atitit 错误处理机制:(1)静默模式(2)警告模式 (3)异常模式 三. PDO的错误处理机制: (1)静默模式 默认情况下与mysql处理方式一致,不现实错误信息(静默模式 ...
- 获取应用程序根目录物理路径(Web and Windows)
这两个计划写一个小类库,需要在不同项目下任意调用.该类库需要对磁盘文件进行读写,所以就需要获取程序执行的磁盘路径,就简单的对获取磁盘路径的方法进行研究. 借助搜索引擎,我从网上搜罗来多种方法,都可以直 ...
- 《深入应用C++11:代码优化与工程级应用》勘误表
<深入应用C++11:代码优化与工程级应用>勘误表,会不断更新,欢迎读者留言或发邮件(cpp11book@163.com)给我提出宝贵意见. 1.第7.3节目录final和override ...
- golang:slice切片
一直对slice切片这个概念理解的不是太透彻,之前学习python的就没搞清楚,不过平时就用python写个工具啥的,也没把这个当回事去花时间解决. 最近使用go开发又遇到这个问题,于是打算彻底把这个 ...
- java框架篇---hibernate之缓存机制
一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...
- rsync用于数据迁移/备份的几个细节
上周我们的一个GitLab服务频繁出现web页面卡死问题,得重启虚拟机才可恢复,但重启之后没多久又会卡死.后来发现是虚拟机的磁盘大小超过了2T,而虚拟机管理那层的文件系统是ext3,最大单文件只能支持 ...
- Foxpro数据库连接错误解决方法--【VFP DBF文件不是一个有效的路径。 确定路径名称拼写是否正确,以及是否连接到文件存放的服务器】
直接访问vfp dbf文件时报错: 错误描述: 'd:\vfpData\test.dbf'不是一个有效的路径. 确定路径名称拼写是否正确,以及是否连接到文件存放的服务器. 解决办法:Data Sour ...
- Spring Data Jpa 使用Left Join
准备: Spring Boot + Web + Jpa 代码: 类:AccountRepository @Query(value = "select new com.sino.report. ...