1. 基本操作
1.1. 重构
为了维持其平衡树的性质,设定参数 \(\alpha\) 决定是否重构。它的具体含义是若 \(\rm \max\{\text{siz}_{lson},\text{siz}_{rson}\}>\alpha\cdot \text{siz}_o\) 就重构 \(o\) 这棵子树。
重构具体就是将子树拍扁成一个序列,然后再重建。
1.2. 插入
插入节点可能会使平衡树不再平衡,我们从插入节点 \(u\)(叶子节点)向上遍历,选择一个子树最大的 \(u\) 的祖先进行重构。
1.3. 删除
对每个被删除的点暂时打一个懒标记,并从下至上去掉这个点的信息。一种延迟删除的方式是在每次删除后判断被删除的点是否已经达到 \(n/2\),如果达到就整棵树重构。
2. 时间复杂度
2.1. 插入
假设一个刚被重构的子树大小为 \(x\),最坏的情况显然是一直向它的某个子树插点。不妨设插入 \(d\) 次后需要再次重构这棵子树,那么有:
\[x/2+d>\alpha\cdot (x+d)\Rightarrow d>\frac{\alpha-1/2}{1-\alpha}\cdot x
\]
因此至少每 \(\frac{\alpha-1/2}{1-\alpha}\cdot x\) 进行一次重构,而重构的复杂度是 \(\mathcal O(x+d)\),所以均摊重构复杂度为:
\[\frac{\mathcal O(x+\frac{\alpha-1/2}{1-\alpha}\cdot x)}{\frac{\alpha-1/2}{1-\alpha}\cdot x}=\mathcal O\left(\frac{1-\alpha}{\alpha-1/2}+1\right)
\]
当 \(\alpha=0.75\) 时均摊为 \(\mathcal O(2)\). 所以插入操作就是朴素复杂度也即 \(\mathcal O(\log n)\),可以发现,\(\alpha\) 越大时均摊时间越小,但同时树也会变得不平衡。
2.2. 删除
\[\frac{\frac{n}{2}\cdot \mathcal O(\log n)+\mathcal O(n)}{\frac{n}{2}}=\mathcal O(\log n)+\mathcal O(2)
\]
2.3. 其他常见平衡树操作
因为保证树高是 \(\log n\) 的,所以复杂度基本都能保证。
3. 代码实现
真它喵的调麻了,一直 \(\rm T\),一直 \(\rm T\),你它喵的一直 \(\rm T\)!结果最后发现是 Del()
中符号写反了,焯(t[rt].siz
不是删去的节点啊长点心吧)!
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f |= (s=='-');
while(s>='0' and s<='9')
x = (x<<1)+(x<<3)+(s^48),
s = getchar();
return f?-x:x;
}
template <class T>
inline void write(T x) {
static int writ[40],w_tp=0;
if(x<0) putchar('-'),x=-x;
do writ[++w_tp]=(x-x/10*10),x/=10; while(x);
while(putchar(writ[w_tp--]^48),w_tp);
}
#include <iostream>
using namespace std;
const bool ftcy = true;
const int maxn = 1e5+5;
const double alpha = 0.75;
int rt,seq[maxn],sq;
int rec[maxn],tp,idx,all;
struct node {
bool ban;
int fa,son[2],siz,v;
} t[maxn];
bool isbad(int o) {
return alpha*t[o].siz<t[t[o].son[0]].siz ||
alpha*t[o].siz<t[t[o].son[1]].siz;
}
int NewNode(int val,int f) {
int id;
if(tp) id = rec[tp --];
else id = ++ idx;
t[id].siz=1; t[id].ban=0;
t[id].v=val, t[id].fa=f;
return id;
}
void build(int &o,int l,int r) {
if(l>r) return;
int mid = l+r>>1; o=seq[mid];
build(t[o].son[0],l,mid-1);
build(t[o].son[1],mid+1,r);
if(t[o].son[0]) t[t[o].son[0]].fa = o;
if(t[o].son[1]) t[t[o].son[1]].fa = o;
t[o].siz = t[t[o].son[0]].siz+t[t[o].son[1]].siz+1;
}
void pia(int o) {
if(!o) return;
pia(t[o].son[0]);
if(!t[o].ban) seq[++ sq] = o;
else rec[++ tp] = o, -- all;
pia(t[o].son[1]);
t[o].son[0]=t[o].son[1]=0;
}
void rebuild(int o) {
// the original value of nw is important
int f=t[o].fa,d=(t[f].son[1]==o),nw=0;
sq=0; pia(o);
build(nw,1,sq);
if(f) t[f].son[d]=nw;
t[nw].fa = f;
if(o==rt) rt=nw;
}
void Print(int o) {
if(!o) return;
printf("%d edge (%d, %d)\n",t[o].v,t[t[o].son[0]].v,t[t[o].son[1]].v);
Print(t[o].son[0]);
Print(t[o].son[1]);
}
void check(int x,int val) {
int s = val<=t[x].v?0:1;
while(t[x].son[s]) {
if(isbad(t[x].son[s])) {
rebuild(t[x].son[s]);
return;
}
x = t[x].son[s], s = val<=t[x].v?0:1;
}
}
void ins(int x) {
++ all;
if(!rt) return rt=NewNode(x,0),void();
int o=rt;
while(ftcy) {
++ t[o].siz; // update the size!!!
int f=o,d=(x>t[o].v); o=t[o].son[d];
if(!o) {
t[f].son[d]=NewNode(x,f);
break;
}
}
check(rt,x);
}
void del(int rk) {
int o=rt;
while(ftcy) {
-- t[o].siz; // update the size!!!
if(!t[o].ban && t[t[o].son[0]].siz+1==rk) {
t[o].ban = 1; return;
}
if(t[t[o].son[0]].siz+(t[o].ban^1)>=rk)
o = t[o].son[0];
else {
rk -= t[t[o].son[0]].siz+(t[o].ban^1);
o = t[o].son[1];
}
}
}
int rnk(int x) {
int ret=0,o=rt;
while(o) {
if(x<=t[o].v) o=t[o].son[0];
else ret+=t[t[o].son[0]].siz+(t[o].ban^1),o=t[o].son[1];
}
return ret+1;
}
// all: nodes on the tree
// t[rt].siz: nodes that truly exist
void Del(int x) {
del(rnk(x));
if(0.5*all>t[rt].siz)
rebuild(rt);
}
int kth(int x) {
int o=rt;
while(ftcy) {
if(x<=t[t[o].son[0]].siz)
o=t[o].son[0];
else {
x -= t[t[o].son[0]].siz+(t[o].ban^1);
if(!x) return t[o].v;
o=t[o].son[1];
}
}
}
int main() {
for(int n=read(9);n;--n) {
int op = read(9), x = read(9);
if(op==1) ins(x);
else if(op==2) Del(x);
else if(op==3) print(rnk(x),'\n');
else if(op==4) print(kth(x),'\n');
else if(op==5) print(kth(rnk(x)-1),'\n');
else print(kth(rnk(x+1)),'\n');
}
return 0;
}