【bzoj3224】平衡樹模板(Splay)
阿新 • • 發佈:2019-02-03
bzoj 3224 普通平衡樹 包含此模板全部操作,可以提交至此。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=500005;
int fa[maxn],ch[maxn][2],dat[maxn],cnt[maxn],siz[maxn],sz,root;
//fa[i]表示i的父結點,ch[i][0]表示i的左兒子,ch[i][1]表示i的右兒子,
//dat[i]表示i的關鍵字(即結點i代表的那個數字),cnt[i]表示i結點的關鍵字出現的次數(相當於權值),
//siz[i]表示包括i的這個子樹的大小;sz為整棵樹的大小,root為整棵樹的根
int n;
inline void clear(int x){//將當前點的各項值都清0
ch[x][0]=ch[x][1]=fa[x]=cnt[x]=dat[x]=siz[x]=0;
}
inline int get(int x){//判斷當前點是它父結點的左兒子還是右兒子
return ch[fa[x]][1]==x;
//左兒子返回0,右兒子返回1;
}
inline void update(int x){//更新當前點的siz值
if(x){
siz[x]=cnt[x];//定為該點的權值
if(ch[x][0])siz[x]+=siz[ch[x][0]];//左兒子的大小
if(ch[x][1])siz[x]+=siz[ch[x][1]];//右兒子的大小
}
}
inline void rotate(int x){//將x結點rotate到它的父親的位置。
int old=fa[x],oldf=fa[old],wh=get(x);
//找出x與父親的位置關係
ch[old][wh]=ch[x][wh^1];
fa[ch[old][wh]]=old;
fa[old]=x;
ch[x][wh^1 ]=old;
//將原先父親更新到x的對應兒子的位置,將x原先對應兒子更新到x原先父親的對應兒子;
fa[x]=oldf;
if(oldf){
ch[oldf][ch[oldf][1]==old]=x;
}
//將x更新到原先父親的位置;
update(old);
update(x);
//更新子樹大小有變化的節點。
} splay(int x){//將x結點rotate到根。
for(int f;f=fa[x];rotate(x)){
if(fa[f])rotate((get(x)==get(f)?f:x));
//分類討論,如果x與父親,祖父三點共線,則需要rotate父親,否則rotate x,避免出現平衡樹失衡,可以嘗試手動證明。
}
root=x;//更新根節點
}
inline void insert(int v){//插入值為v的點
if(root==0){
//子樹為空則特殊處理
sz++;ch[sz][0]=ch[sz][1]=fa[sz]=0;
dat[sz]=v;cnt[sz]=1;siz[sz]=1;root=sz;
return;
}
int now=root,f=0;
while(1){
if(dat[now]==v){
//若存在v,則增加該節點權值
cnt[now]++;
update(now);
update(f);
splay(now);
break;
}
f=now;
now=ch[now][dat[now]<v];
if(now==0){
//若不存在,則新增節點。
sz++;
ch[sz][0]=ch[sz][1]=0;
dat[sz]=v;
siz[sz]=1;
cnt[sz]=1;
fa[sz]=f;
ch[f][dat[f]<v]=sz;
update(f);
splay(sz);
break;
}
}
}e int find(int v){//查詢v的排名
int ans=0,now=root;
while(1){
if(v<dat[now])now=ch[now][0];
else{
ans+=(ch[now][0]?siz[ch[now][0]]:0);
if(v==dat[now]){splay(now);return ans+1;}
ans+=cnt[now];
now=ch[now][1];
}
}
}
inline int findx(int x){//找到排名為x的點
int now=root;
while(1){
if(ch[now][0]&&x<=siz[ch[now][0]]){
now=ch[now][0];
}else{
int tmp=(ch[now][0]?siz[ch[now][0]]:0)+cnt[now];
if(x<=tmp)return dat[now];
x-=tmp;
now=ch[now][1];
}
}
}
inline int pre(){//查詢樹上的前驅
int now=ch[root][0];
while(ch[now][1])now=ch[now][1];
return now;
}
inline int nxt(){//查詢樹上的後繼
int now=ch[root][1];
while(ch[now][0])now=ch[now][0];
return now;
}
inline void del(int x){//刪除節點x
int wh=find(x);//把x旋轉到根
//分類討論每一種情況
if(cnt[root]>1){cnt[root]--;return;}
if(!ch[root][0]&&!ch[root][1]){clear(root);root=0;return;}
if(!ch[root][0]){
int oldr=root;
root=ch[root][1];
fa[root]=0;
clear(oldr);
return;
}else if(!ch[root][1]){
int oldr=root;
root=ch[root][0];
fa[root]=0;
clear(oldr);
return;
}
//若兩個兒子都有,則把x的前驅splay到樹根,再把原來根節點的右兒子接到現在的根上,此時原先的根就變成了葉子節點,直接刪除即可。
int lb=pre();
int oldr=root;
splay(lb);
fa[ch[oldr][1]]=root;
ch[root][1]=ch[oldr][1];
clear(oldr);
update(root);
return;
}
inline int prex(int x){//查x的前驅
//查詢x的前驅即插入x,查詢樹上的前驅(此時x為root),然後刪除x的過程,後繼同理。
insert(x);
int ans=pre();
del(x);
return dat[ans];
}
inline int nxtx(int x){//查x的後繼
insert(x);
int ans=nxt();
del(x);
return dat[ans];
}
int main(){
scanf("%d",&n);
int opt,xx;
while(n--){
scanf("%d%d",&opt,&xx);
if(opt==1){
insert(xx);
continue;
}
if(opt==2){
del(xx);
continue;
}
if(opt==3){
printf("%d\n",find(xx));
continue;
}
if(opt==4){
printf("%d\n",findx(xx));
continue;
}
if(opt==5){
printf("%d\n",prex(xx));
continue;
}
if(opt==6){
printf("%d\n",nxtx(xx));
continue;
}
}
return 0;
}