传送门
替罪羊树,优秀的数据结构,关键思想是 假如这棵树长残了就拍扁重构成一棵二叉树,常数很小。alpha是一个平衡因子,用来判断这棵树是否长残,一般取0.5~0.9,比较玄学。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int MAXN = 100005;
const double alpha = 0.75;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
int memory[MAXN]; //内存池
int cur[MAXN],m; //拍扁的时候用的内存空间
int rt,pool,poi,cnt,to_rebuild;
int ch[MAXN][2],val[MAXN],siz[MAXN],tot[MAXN]; //siz表示现存的子节点,tot表示总共的
bool exist[MAXN];
inline bool isbad(int x){
if((double)siz[x]*alpha<=(double)max(siz[ch[x][0]],siz[ch[x][1]])) return true;
return false;
}
void dfs(int x){
if(!x) return;dfs(ch[x][0]);
if(exist[x]) cur[++poi]=x;
else memory[++pool]=x;
dfs(ch[x][1]);
}
void build(int &x,int l,int r){
int mid=l+r>>1;
x=cur[mid];
if(l==r) {
ch[x][0]=ch[x][1]=0;
siz[x]=tot[x]=1;
return;
}
if(l<mid) build(ch[x][0],l,mid-1);
else ch[x][0]=0;
build(ch[x][1],mid+1,r);
tot[x]=tot[ch[x][0]]+tot[ch[x][1]]+1;
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
inline void rebuild(int &x){
poi=0;dfs(x);
if(poi) build(x,1,poi);
else x=0;
}
void Insert(int &now,int x){
if(!now) {
now=memory[pool--];val[now]=x;
exist[now]=tot[now]=siz[now]=1;
ch[now][0]=ch[now][1]=0;
return ;
}
tot[now]++,siz[now]++;
if(val[now]>=x) Insert(ch[now][0],x);
else Insert(ch[now][1],x);
bool bad=isbad(now);
if(!bad && to_rebuild){
if(ch[now][0]==to_rebuild) rebuild(ch[now][0]);
else rebuild(ch[now][1]);
to_rebuild=0;
}
else if(bad) to_rebuild=now;
}
int rk(int x){
int now=rt,ret=1;
while(now){
if(val[now]>=x) now=ch[now][0];
else {
ret+=siz[ch[now][0]]+exist[now];
now=ch[now][1];
}
}
return ret;
}
int kth(int x){
int now=rt;
while(now){
if(exist[now] && siz[ch[now][0]]+1==x) return val[now];
else if(siz[ch[now][0]]>=x) now=ch[now][0];
else x-=siz[ch[now][0]]+exist[now],now=ch[now][1];
}
}
void Erase_pos(int &now,int x){
if(exist[now] && siz[ch[now][0]]+1==x){
exist[now]=0;siz[now]--;
return;
}
siz[now]--;
if(siz[ch[now][0]]+exist[now]>=x) Erase_pos(ch[now][0],x);
else Erase_pos(ch[now][1],x-siz[ch[now][0]]-exist[now]);
}
void Erase_val(int x){
Erase_pos(rt,rk(x));
if((double)tot[rt]*alpha>siz[rt]) rebuild(rt);
}
int main(){
for(int i=100000;i;i--) memory[++pool]=i;
m=rd();int opt,x;
while(m--){
opt=rd(),x=rd();
if(opt==1) {Insert(rt,x);if(isbad(rt)) rebuild(rt);}
if(opt==2) Erase_val(x);
if(opt==3) printf("%d\n",rk(x));
if(opt==4) printf("%d\n",kth(x));
if(opt==5) printf("%d\n",kth(rk(x)-1));
if(opt==6) printf("%d\n",kth(rk(x+1)));
}
return 0;
}