学习笔记(4)Splay
·\(Splay\)(伸展树)
是一种通过将所有操作转换至根节点位置以改变树的结构,保证均摊时间复杂度(操作均为 \(O(log_{n})\) )的一种平衡树,主要通过上旋操作 \((rotate)\) 与双旋操作 \((splay)\) 维持树的结构与 \(bst\) 性质
更形象点说,该平衡树将“更频繁使用”的数据放在根节点及其临近位置,那么对于一些操作可以适当“偷懒”
比如:
·例题:
\(P3369\) 【模板】普通平衡树
住意由于 \(remove\) 操作会先调用一次 \(find\)__\(rank\) 操作及 \(pre\) 操作,因而将其写在最后面。同时写封装的话可以避免在需要多棵树的时候复制粘贴大段代码
纯模板题,code:
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n;
struct Splay{
int root,tot;
int val[N],cnt[N],par[N],siz[N],son[N][2];
int add(int k){
val[++tot]=k;
par[tot]=son[tot][0]=son[tot][1]=0;
siz[tot]=cnt[tot]=1;
return tot;
}
int which(int now){return son[par[now]][1]==now;}
void clean(int now){val[now]=cnt[now]=par[now]=son[now][0]=son[now][1]=siz[now]=0;}
void pushup(int now){siz[now]=cnt[now]+siz[son[now][0]]+siz[son[now][1]];}
void rotate(int now){
int fa=par[now];
int anc=par[fa];
int a=which(now), b=which(fa);
par[son[now][a^1]]=fa;
son[fa][a]=son[now][a^1]; son[now][a^1]=fa;
par[now]=anc; par[fa]=now;
if(anc) son[anc][b]=now;
pushup(fa);
pushup(now);
}
void splay(int now){
for(int fa;fa=par[now];rotate(now)){
if(par[fa]) rotate(which(now)==which(fa)? fa:now);
}
root=now;
}
void insert(int k){
int now=root, fa=0;
while(val[now]!=k && now){
fa=now;
now=son[now][val[now]<k];
}
if(now){
++cnt[now];
pushup(now);
pushup(fa);
}
else{
now=add(k);
if(fa) son[fa][val[fa]<k]=now;
par[now]=fa;
}
splay(now);
}
int find_num(int k){
int now=root;
while(1){
if(son[now][0] && k<=siz[son[now][0]]) now=son[now][0];
else if(k<=siz[son[now][0]]+cnt[now]){splay(now); return val[now];}
else k-=siz[son[now][0]]+cnt[now], now=son[now][1];
}
}
int find_rank(int k){
int now=root, ans=0;
while(1){
if(!now) return 0;
if(k<val[now]) now=son[now][0];
else if(ans+=siz[son[now][0]], k==val[now]){splay(now); return ans+1;}
else ans+=cnt[now], now=son[now][1];
}
}
int pre(){
int now=son[root][0];
while(son[now][1]) now=son[now][1];
splay(now);
return now;
}
int suf(){
int now=son[root][1];
while(son[now][0]) now=son[now][0];
splay(now);
return now;
}
void remove(int k){
int pos=find_rank(k);
if(!pos) return;
if(cnt[root]>1){
--cnt[root]; pushup(root); return;
}
if(!son[root][0] && !son[root][1]){
clean(root); root=0; return;
}
if(!son[root][0] || !son[root][1]){
int old=root;
root=son[root][0]+son[root][1];
clean(old); par[root]=0; return;
}
int old=root;
pre();
son[root][1]=son[old][1];
par[son[old][1]]=root;
clean(old); par[root]=0; pushup(root);
}
}T;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int op,x;
scanf("%d%d",&op,&x);
switch(op){
case 1: T.insert(x); break;
case 2: T.remove(x); break;
case 3: T.insert(x); printf("%d\n",T.find_rank(x)); T.remove(x); break;
case 4: printf("%d\n",T.find_num(x)); break;
case 5: T.insert(x); printf("%d\n",T.val[T.pre()]); T.remove(x); break;
case 6: T.insert(x); printf("%d\n",T.val[T.suf()]); T.remove(x); break;
}
}
return 0;
}