不简单平衡树
Splay算法
旋转操作
代码如下:
bool get(int x) {
return son[f[x]][1]==x;
}
void update(int x) {
if(x){
siz[x]=cou[x];
if(son[x][0])siz[x]+=siz[son[x][0]];
if(son[x][1])siz[x]+=siz[son[x][1]];
}
}
void rotate(int x) {
int old=f[x],oldf=f[old],whichson=get(x);
son[old][whichson]=son[x][whichson^1],f[son[old][whichson]]=old;
son[x][whichson^1]=old,f[old]=x;
f[x]=oldf;
if(oldf) {
son[oldf][son[oldf][1]==old]=x;
}
update(old);
update(x);
}
splay操作
将
x
x
x旋到根节点以保证均摊复杂度
代码如下:
void splay(int x) {
for(int fa;fa=f[x];rotate(x))
if(f[fa])
rotate((get(x)==get(fa))?fa:x);
root=x;
}
插入操作
加入节点。
1.如果
r
o
o
t
root
root=
0
0
0,说明树为空,特判加入即可。
2.如果 不为空,则
(1). 如果存在于x权值相同的节点,那么
c
o
u
[
cou[
cou[节点标号
]
]
]++,再用
s
p
l
a
y
splay
splay维护一下
s
i
z
siz
siz
(2). 如果不存在,那么新开一个节点,再用
s
p
l
a
y
splay
splay维护一下
s
i
z
siz
siz
void insert(int x) {
if(root==0) {
sz++;
son[sz][0]=son[sz][1]=f[sz]=0;
key[sz]=x;
cou[sz]=siz[sz]=1;
root=sz;
return;
}
int now=root,fa=0;
while(1) {
if(x==key[now]) {
cou[now]++;
update(now);
update(fa);
splay(now);
break;
}
fa=now;
now=son[now][key[now]<x];
if(now==0) {
sz++;
f[sz]=fa;
son[sz][0]=son[sz][1]=0;
cou[sz]=siz[sz]=1;
son[fa][key[fa]<x]=sz;
key[sz]=x;
update(fa);
splay(sz);
break;
}
}
}
查询一个数的排名
- 如果 x x x小于 k e y [ n o w ] key[now] key[now],就寻找 n o w now now的左儿子
- 如果 x x x等于 k e y [ n o w ] key[now] key[now],就直接 r e t u r n ( a n s return (ans return(ans+ 1 ) 1) 1)
- 如果 x x x大于 k e y [ n o w ] key[now] key[now],那么 a n s ans ans+= s i z [ s o n [ n o w ] [ 0 ] ] siz[son[now][0]] siz[son[now][0]]+ c o u [ n o w ] cou[now] cou[now],然后寻找 n o w now now的右儿子
int find(int x){
int now=root,ans=0;
while(1){
if(x<key[now]) now=son[now][0];
else{
ans+=(son[now][0]?siz[son[now][0]]:0);
if(x==key[now]) {
splay(now);
return ans+1;
}
ans+=cou[now];
now=son[now][1];
}
}
}
查询排名为k的数
- 如果 n o w now now左儿子的大小> x x x,则直接寻找 n o w now now的左儿子
- 如果 n o w now now左儿子的大小和 n o w now now的大小之和大于等于 x x x,则 k e y [ n o w ] key[now] key[now]就是答案
- 如果 n o w now now左儿子的大小和 n o w now now的大小之和小于 x x x,则 x − = n o w x-=now x−=now左儿子的大小和 n o w now now的大小之和,然后在 n o w now now右子树里寻找第x大的元素
int kth(int x) {
int now=root;
while(1) {
if(son[now][0] && x<=siz[son[now][0]]) {
now=son[now][0];
} else {
int temp=(son[now][0]?siz[son[now][0]]:0)+cou[now];
if(x<=temp)return key[now];
x-=temp;
now=son[now][1];
}
}
}
前驱、后继
int pre(){
int now=son[root][0];
while(son[now][1])now=son[now][1];
return now;
}
int next(){
int now=son[root][1];
while(son[now][0])now=son[now][0];
return now;
}
删除节点
void del(int x){
int whatever=find(x);
if(cou[root]>1) {
cou[root]--;
update(root);
return;
}
if(!son[root][0]&&!son[root][1]) {
clear(root);
root=0;
return;
}
if(!son[root][0]) {
int oldroot=root;
root=son[root][1];
f[root]=0;
clear(oldroot);
return;
}
if(!son[root][1]) {
int oldroot=root;
root=son[root][0];
f[root]=0;
clear(oldroot);
return;
}
int leftbig=pre(),oldroot=root;
splay(leftbig);
son[root][1]=son[oldroot][1];
f[son[root][1]]=root;
clear(oldroot);
update(root);
return;
}
Treap算法
T
r
e
a
p
Treap
Treap=
t
r
e
e
tree
tree+
h
e
a
p
heap
heap
在
S
p
l
a
y
Splay
Splay的基础上多记录一个随机值
r
d
[
i
]
rd[i]
rd[i],表示改点的随机值,要求随机值排成一个大根堆,来保证树的深度。(可选择不记录
f
a
t
h
e
r
father
father
旋转操作
与 s p l a y splay splay类似,不同的是 r o t a t e rotate rotate函数有不同:
void rotate(int &x,int y){
int i=son[x][y^1];
son[x][y^1]=son[i][y];
son[i][y]=x;
push_up(x);
push_up(i);
x=i;
}
插入操作
void ins(int p,int x){
if(!p){
p=++sz;
siz[p]=cnt[p]=1;
key[p]=x;
rd[p]=rand();
return;
}
if(key[p]==x){
cnt[p]++;
siz[p]++;
return;
}
int d=(x>key[p]);
ins(son[p][d],x);
if(rd[p]<td[son[p][d]))
rotate(p,d^1);
push_up(p);
}
删除操作
void del(int &p,int x){
if(!p) return;
if(x!=key[p]) del(son[o][x>key[p]],x);
else{
if(!son[p][0]&&!son[p][1]){
cnt[p]--;
siz[p]--;
if(!cnt[p]) p=0;
}
else if(son[p][0]&&!son[p][1]){
rotate(p,1);
del(son[p][1],x);
}
else if(!son[p][0]&&son[p][1]){
rotate(p,0);
del(son[p][0],x);
}
else{
iny d=rd[son[p][0]>rd[son[p][1]];
rotate(p,d);
del(son[p][d],x);
}
}
push_up(p);
}
前驱、后继
int pre(int p,int x){
if(!p) return -INF;
if(key[p]>=x) return pre(son[p][0],x);
else return max(key[p],pre(son[p][1],x));
}
int suf(int p,int x){
if(!p) return INF;
if(key[p]<=x) return suf(son[p][1],x);
else return min(key[p],pre(son[p][0],x));
}
查询操作与splay基本相同,不再介绍。
无旋Treap
因为
t
r
e
a
p
treap
treap的常数远小于
s
p
l
a
y
splay
splay,而难以进行区间操作。
所以,我们引入了无旋
t
r
e
a
p
treap
treap,又称
F
H
Q
t
r
e
a
p
FHQ treap
FHQtreap。
支持可持久化。
分裂
void split(int rt,int &a,int &b,int v){
if(!rt){a=b=0;return;}
if(t[rt].v<=v){
a=rt;
split(t[rt].rc,t[a].rc,b,v);
}
else{
b=rt;
split(t[rt].lc,a,t[b].lc,v);
}
update(rt);
}
合并
void merge(int &rt,int a,int b){
if(!a||!b){rt=a+b;return;}
if(t[a].rnk<t[b].rnk){
rt=a;
merge(t[rt].rc,t[a].rc,b);
}
else{
rt=b;
merge(t[rt].lc,a,t[b].lc);
}
}