替罪羊树
替罪羊树是一种不用旋转的平衡树,并且速度还不错,大约在treap和splay之间吧(treap就是那么快)。
这里用rank和xth操作实现了pre和next操作。注意rank操作找的是>=x且最小的编号,这样pre只需要查找rank(x)-1,next只需要查找rank(x+1),很优雅。
具体证明复杂度见知乎。(我不会啊)
#include <cstdio>
using namespace std;
const int maxn=1e5+5; const double alpha=0.7;
struct ScpGoatTree{ //v:元素值 cnt:个数 siz:子树大小
int root, cntn, q[maxn], cntq;
int l[maxn], r[maxn], v[maxn], cnt[maxn], siz[maxn];
bool bad(int x){
int t=siz[x]*alpha+5;
if (siz[l[x]]>t||siz[r[x]]>t) return true;
return false;
}
void clear(int x){ l[x]=r[x]=siz[x]=0; }
void up(int x){ siz[x]=siz[l[x]]+siz[r[x]]+cnt[x]; }
void dfs(int now){
if (!now) return;
dfs(l[now]);
if (cnt[now]) q[++cntq]=now;
dfs(r[now]);
}
void build(int &now, int L, int R){ //now被替换成区间中点
if (L>R) return;
int mid=(L+R)/2; now=q[mid]; clear(now); //一定要清空节点!以免访问到原有的左右孩子
build(l[now], L, mid-1); build(r[now], mid+1, R);
up(now);
}
void rebuild(int &now){ cntq=0; dfs(now); build(now, 1, cntq); }
void modify(int &now, int x, int c){ //注意now是父节点的孩子的引用,因此可以直接改变
if (!now) now=++cntn, v[now]=x; //如果没有这个节点就先新建一个(只有插入操作可能触发)
siz[now]+=c;
if (x==v[now]){ cnt[now]+=c; return; }
if (x<v[now]) modify(l[now], x, c);
if (x>v[now]) modify(r[now], x, c);
if (bad(now)) rebuild(now);
}
int rank(int now, int x){ //找>=x的点的排名。
//如果要写<=x的点的排名,那么会不优雅
int ans=0;
while (now){
if (x==v[now]) return ans+siz[l[now]]+1;
if (x<v[now]) now=l[now];
if (x>v[now]) ans+=siz[l[now]]+cnt[now], now=r[now];
}
return ans+1;
}
int xth(int now, int x){
int need;
while (now){
need=x-siz[l[now]]; //除了左子树还需要的点
if (need>0&&need<=cnt[now]) return v[now];
if (need<=0) now=l[now];
else x-=siz[l[now]]+cnt[now], now=r[now];
}
return -1;
}
void printtree(){
printf("%d
", v[root]);
for (int i=1; i<=cntn; ++i)
printf("%d %d %d
", v[i], v[l[i]], v[r[i]]);
puts("");
}
}sgt;
int n, op, x;
int main(){
scanf("%d", &n);
for (int i=1; i<=n; ++i){
scanf("%d%d", &op, &x);
if (op==1) sgt.modify(sgt.root, x, 1);
if (op==2) sgt.modify(sgt.root, x, -1);
if (op==3) printf("%d
", sgt.rank(sgt.root, x));
if (op==4) printf("%d
", sgt.xth(sgt.root, x));
if (op==5){
int k=sgt.rank(sgt.root, x)-1;
printf("%d
", sgt.xth(sgt.root, k));
}
if (op==6){
int k=sgt.rank(sgt.root, x+1);
printf("%d
", sgt.xth(sgt.root, k));
}
//sgt.printtree();
}
return 0;
}