题目链接: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]的更多相关文章

  1. Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...

  2. BZOJ 3224 普通平衡树(Treap模板题)

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 14301  Solved: 6208 [Submit][ ...

  3. [洛谷P3369] 普通平衡树 Treap & Splay

    这个就是存一下板子...... 题目传送门 Treap的实现应该是比较正经的. 插入删除前驱后继排名什么的都是平衡树的基本操作. #include<cstdio> #include< ...

  4. BZOJ 3224 普通平衡树

    这个是第一份完整的treap代码.嗯...虽然抄的百度的版,但还是不错的. !bzoj上不能用srand. #include<iostream>#include<cstdio> ...

  5. Luogu 3369 / BZOJ 3224 - 普通平衡树 - [替罪羊树]

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...

  6. BZOJ 3224 普通平衡树 | 平衡树模板

    #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> # ...

  7. bzoj 3224 普通平衡树 vactor的妙用

    3224: Tyvj 1728 普通平衡树 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/ ...

  8. [bzoj 3224]手写treap

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3224 bzoj不能用time(0),看到这个博客才知道,我也RE了好几发…… #inclu ...

  9. BZOJ 3224 普通平衡树(树状数组)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=3224 题意:维护以下操作:(1)插入x:(2)删除x(若有多个相同的数,只删除一个)(3 ...

随机推荐

  1. Chrome 调试技巧: 调整网速

    为了方便调试某些内容,比如我想网速设置为 1kb每秒,甚至0kb每秒. 1.打开chrome DevTool ,切换到 "Network".找到最右侧 "Online&q ...

  2. 在 Redis 上实现的分布式锁

    由于近排很忙,忙各种事情,还有工作上的项目,已经超过一个月没写博客了,确实有点惭愧啊,没能每天或者至少每周坚持写一篇博客.这一个月里面接触到很多新知识,同时也遇到很多技术上的难点,在这我将对每一个有用 ...

  3. ceph 的 bufferlist

    bufferlist是buffer::list的别名,其由来在 http://bean-li.github.io/bufferlist-in-ceph/ 中有非常详细的介绍 其p.p_off.off字 ...

  4. linux每日命令(29):chown命令

    chown将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户ID:组可以是组名或者组ID:文件是以空格分开的要改变权限的文件列表,支持通配符.系统管理员经常使用chown命令,在将文件拷贝 ...

  5. 【iCore1S 双核心板_ARM】例程八:ADC实验——电源监控

    实验原理: STM32内部集成三个12位ADC,iCore1S的所有电源经过 电阻分压或者直接接入STM32的ADC的输出通道内,输入电流 经过高端电流检测芯片ZXCT1009F输入到ADC的输入通道 ...

  6. 聊聊模板方法模式,装饰器模式以及AOP

    在软件系统设计的时候,我们需要把一个大的系统按照业务功能进行拆分,做到高内聚.低耦合. 但是呢,拆分之后会产生一些通用性的东西,比如日志,安全,事务,性能统计等,这些非功能性需求,横跨多个模块.最lo ...

  7. mysql解决大量time_wait

    mysql解决大量time_wait     命令查看TIME_WAIT连接数 netstat -ae|grep "TIME_WAIT" |wc -l 早上登陆服务器的时候输入ne ...

  8. Matlab如何循环读取文件

    循环读取图片第一种方法①List =dir('*.jpg'); %如需其它图片格式支持,可以自己[重载dir()]函数,实现查找所有图片文件的功能,%如果图片是其它路径,可以用 ["路径&q ...

  9. linux下Ftp服务安装

    安装vsftp 使用yum命令安装vsftp #yum install vsftpd -y 如果yum安装不成功,可以到 http://pkgs.org/centos-6/centos-x86_64/ ...

  10. Zoomit的用法总结

    今天才发现Zoomit,相见恨晚.总结一下zoomit的使用方法,备用. Zoomit是一款超赞的演示辅助工具.具有屏幕缩放.屏幕画笔.倒计时功能.且无需安装,点开即用. 1. 屏幕缩放 Ctrl + ...