[P3369 【模板】普通平衡树] 平衡树Splay
一个惨痛的教训
题目
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 x x x数
- 删除 x x x数(若有多个相同的数,因只删除一个)
- 查询 x x x数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1)
- 查询排名为 x x x的数
- 求 x x x的前驱(前驱定义为小于 x x x,且最大的数)
- 求 x x x的后继(后继定义为大于 x x x,且最小的数)
输入格式
第一行为 n n n,表示操作的个数,下面 n n n行每行有两个数 o p t opt opt和 x x x, o p t opt opt表示操作的序号( 1 ≤ o p t ≤ 6 1≤opt≤6 1≤opt≤6 )
输出格式
对于操作 3 , 4 , 5 , 6 3,4,5,6 3,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入 #1
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出 #1
106465
84185
492737
说明/提示
【数据范围】
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
1
0
5
1\le n \le 10^5
1≤n≤105,
∣
x
∣
≤
1
0
7
∣x∣≤10^7
∣x∣≤107
介绍
(有待补充,现在还没有完全理解,只是挂个板子——2020.1.20)
板子
#include<bits/stdc++.h>
using namespace std;
const int inf=1e8;
int n;
int read(void);
struct node{int fa,val,tot,siz,ch[2];};
class SPLAY{
public:
static const int N=4e5+10;
int tot,root;
node t[N];
bool son(int);
void connect(int,int,int);
int make(int,int);
void update(int);
void rotate(int);
void Splay(int,int);
void insert(int);
int find(int);
void delet(int);
int rak(int);
int kth(int);
int lower(int);
int upper(int);
}T;
int main(){
// clock_t as,bs;
// as=clock();
//freopen("P3369_12.in","r",stdin);freopen("test.out","w",stdout);
n=read();
while(n--){
int op=read(),x=read();
switch(op){
case 1:T.insert(x);break;
case 2:T.delet(x);break;
case 3:printf("%d\n",T.rak(x));break;
case 4:printf("%d\n",T.kth(x));break;
case 5:printf("%d\n",T.lower(x));break;
case 6:printf("%d\n",T.upper(x));break;
}
}
// bs=clock();
// double h=(double)(bs-as)/CLOCKS_PER_SEC;
//printf("%lf",h);
return 0;
}
inline int read(){
char ch=getchar();
int x=0,m=1;
while(!isdigit(ch)){if(ch=='-')m=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*m;
}
inline bool SPLAY::son(int x){
return x==t[t[x].fa].ch[1];
}
inline void SPLAY::connect(int x,int fa,int son){
t[x].fa=fa,t[fa].ch[son]=x;
return;
}
inline int SPLAY::make(int fa,int v){
t[++tot].fa=fa,t[tot].val=v,t[tot].siz=t[tot].tot=1;
return tot;
}
inline void SPLAY::update(int x){
t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+t[x].tot;
return;
}
inline void SPLAY::rotate(int x){
int Y=t[x].fa,R=t[Y].fa;
int Yson=son(x),Rson=son(Y);
connect(t[x].ch[Yson^1],Y,Yson);
connect(Y,x,Yson^1);
connect(x,R,Rson);
update(Y),update(x);
return;
}
inline void SPLAY::Splay(int x,int to){
int tmp=t[to].fa;
while(t[x].fa!=tmp){
int Y=t[x].fa,R=t[Y].fa;
if(tmp!=R)son(x)^son(to)?rotate(x):rotate(Y);
rotate(x);
}
if(to==root)root=x;
return;
}
inline void SPLAY::insert(int x){
int now=root;
if(!now)root=make(0,x);
else{
while(1){
++t[now].siz;
if(t[now].val==x){
t[now].tot++;
Splay(now,root);
return;
}
int nxt=x>=t[now].val;
if(!t[now].ch[nxt]){
t[now].ch[nxt]=make(now,x);
Splay(t[now].ch[nxt],root);
return;
}
now=t[now].ch[nxt];
}
}
return;
}
inline int SPLAY::find(int x){
int now=root;
while(now){
if(t[now].val==x){
Splay(now,root);
return now;
}
int nxt=x>=t[now].val;
now=t[now].ch[nxt];
}
return 0;
}
inline void SPLAY::delet(int x){
int now=find(x);
if(!now)return;
if(t[now].tot>1){
--t[now].tot,--t[now].siz;
return;
}
if(!t[now].ch[0]||!t[now].ch[1]){
root=t[now].ch[0]+t[now].ch[1];
t[root].fa=0;
}
else{
int tmp=t[now].ch[1];
while(t[tmp].ch[0])tmp=t[tmp].ch[0];
Splay(tmp,root);
connect(t[now].ch[0],tmp,0);
connect(tmp,0,1);
update(tmp);
}
return;
}
inline int SPLAY::rak(int x){
int now=root,ans=0;
while(now){
if(t[now].val==x){
ans+=t[t[now].ch[0]].siz+1;
Splay(now,root);
return ans;
}
if(x<t[now].val)now=t[now].ch[0];
else{
ans+=t[t[now].ch[0]].siz+t[now].tot;
now=t[now].ch[1];
}
}
if(now)Splay(now,root);
return 0;
}
inline int SPLAY::kth(int x){
int now=root;
while(1){
int tmp=t[now].siz-t[t[now].ch[1]].siz;
if(t[t[now].ch[0]].siz<x&&x<=tmp){
Splay(now,root);
return t[now].val;
}
if(tmp>x)now=t[now].ch[0];
else x-=tmp,now=t[now].ch[1];
}
Splay(now,root);
return 0;
}
inline int SPLAY::lower(int x){
int now=root,ans=-inf;
while(now){
if(t[now].val<x&&ans<t[now].val)ans=t[now].val;
int nxt=x>t[now].val;
now=t[now].ch[nxt];
}
return ans;
}
inline int SPLAY::upper(int x){
int now=root,ans=inf;
while(now){
if(t[now].val>x&&ans>t[now].val)ans=t[now].val;
int nxt=x>=t[now].val;
now=t[now].ch[nxt];
}
return ans;
}
教训及经验
教训
emmmmm交的很多其实主要原因是没怎么理解,还有就是总是犯一些莫名其妙的小错误。
选择学习模板错误
首先,第一次那几个0和88是找的一个错的模板,它本身就会在最后一个点T掉,更别说我加工过的了。
太懒
要是勤一点多看几遍也许就不用这么受制于模板了。
快读写错了……
inline int read(){
char ch=getchar();
int x=0,m=1;
while(!isdigit(ch))if(ch=='-')m=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*m;
}
原来我的快读长这个鬼样子↑(T__T)↑
乍一看没什么问题吧(而且以前一直这么写没出过什么问题),但是这组数据
10__//以下划线代替空格
1__2
然后后面就再也不能干什么了
所以经过书润大佬的指点(博客)
改成了这个样子
inline int read(){
char ch=getchar();
int x=0,m=1;
while(!isdigit(ch)){if(ch=='-')m=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*m;
}
然后后面T的那两个点(80分程序)就都A了
所以说。。。
我太弱了。
经验
类 class
大概会用class了吧(?)
还有static(?class里面不加过不了编译)
平衡树
历经三天调试,重构三次,终于A了。
对平衡树Splay摸着点皮毛。