有人能告诉我spaly是什么吗
二叉搜索树
保证对于一个点
x
x
x.
v
a
l
[
x
]
>
=
v
a
l
[
l
s
o
n
[
x
]
]
val[x] >= val[lson[x]]
val[x]>=val[lson[x]]且
v
a
l
[
x
]
<
=
v
a
l
[
r
s
o
n
[
x
]
]
val[x]<=val[rson[x]]
val[x]<=val[rson[x]]
定义
s
o
n
son
son儿子节点
f
a
fa
fa父亲节点
v
a
l
val
val节点权值
c
n
t
cnt
cnt权值的个数
s
i
z
e
size
size左右儿子的
s
i
z
e
+
size+
size+自己的
c
n
t
cnt
cnt
一些基础操作
返回是否是右儿子.
更新信息
找到权值为
x
x
x的点并将其旋到根
插入操作
就依照二叉搜索树的性质来
查找
x
x
x的排名
查找排名第
x
x
x的数
查找
x
x
x的前驱或后继(0为前驱1为后继)
一些进阶操作
1.
r
o
t
a
t
e
rotate
rotate
分左旋和右旋
贴一张 盗的 图上来
自己看吧…
2.
s
p
l
a
y
splay
splay
s
p
l
a
y
(
x
,
y
)
splay(x,y)
splay(x,y)指将
x
x
x旋到
y
y
y的儿子.
具体过程还不是很会,贴代码.
3.
d
e
l
e
t
e
delete
delete
找到
x
x
x的前驱和后继,然后利用后继的性质删除即可.
用平衡树处理更多东西
以上所有的操作都是基础操作.
如果要维护一段区间,又怎么用
s
p
l
a
y
splay
splay去做呢?
中序遍历:左根右
由二叉搜索树的定义可知,一棵二叉搜索树的中序遍历是一定且有序的,并按照关键字递增.
所以如果我们要处理
[
l
,
r
]
[l,r]
[l,r]这个区间,我们只需要将
l
−
1
l-1
l−1旋到根,再将
r
+
1
r+1
r+1旋到根的儿子,随后
r
+
1
r+1
r+1的左子树即为满足要求的区间.
随后在
r
+
1
r+1
r+1的左子树打一个标记即可.
当然在
s
p
l
a
y
splay
splay的时候要下传一下当前
s
p
l
a
y
splay
splay节点的标记.
tips:插入一个
−
I
N
F
-INF
−INF和
I
N
F
INF
INF作为
1
1
1号节点和
n
+
2
n+2
n+2号节点,会使代码好写.
代码(Luogu3369普通平衡树)
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 5,INF = 0x3f3f3f3f;
struct Splay
{
int fa,val,size,cnt;
int son[2];
}tr[N];
int rt,tot,n,opt,x;
int get(int x)
{
return tr[tr[x].fa].son[1] == x;
}
void update(int x)
{
tr[x].size = tr[x].cnt + tr[tr[x].son[0]].size + tr[tr[x].son[1]].size;
}
void rotate(int x)
{
int y = tr[x].fa,z = tr[y].fa,kind = !get(x);
tr[y].son[!kind] = tr[x].son[kind];
if (tr[x].son[kind]) tr[tr[x].son[kind]].fa = y;
tr[x].fa = z;
if (z) tr[z].son[get(y)] = x;
tr[x].son[kind] = y,tr[y].fa = x;
update(y),update(x);
}
void splay(int x,int anc)
{
while (tr[x].fa != anc)
{
int y = tr[x].fa;
if (tr[y].fa == anc) rotate(x);
else rotate(get(x) == get(y) ? y : x);
}
if (!anc) rt = x;
}
void insert(int x,int now)
{
int fa = 0;
while (now && tr[now].val != x) { fa = now,now = tr[now].son[x > tr[now].val]; }
if (now) ++tr[now].cnt;
else
{
now = ++tot;
tr[now].size = tr[now].cnt = 1,tr[now].val = x,tr[now].fa = fa;
if (fa) tr[fa].son[x > tr[fa].val] = now;
}
splay(now,0);
}
void find(int x)
{
if (!rt) return;
int now = rt;
while (tr[now].son[x > tr[now].val] && tr[now].val != x) now = tr[now].son[x > tr[now].val];
splay(now,0);
}
int Rank(int x)
{
find(x);
return tr[tr[rt].son[0]].size;
}
int kth(int x)
{
if (x > tr[rt].size) return 0;
int now = rt,pos;
while (1)
{
pos = tr[now].son[0];
if (tr[pos].size + tr[now].cnt < x)
{
x -= tr[pos].size + tr[now].cnt;
now = tr[now].son[1];
}
else if (tr[pos].size >= x) now = pos;
else return tr[now].val;
}
}
int pre_suf(int x,int flag)
{
find(x);
if ((tr[rt].val > x && flag) || (tr[rt].val < x && !flag)) return rt;
int now = tr[rt].son[flag];
while (tr[now].son[!flag]) now = tr[now].son[!flag];
return now;
}
void del(int x)
{
int pre = pre_suf(x,0),suf = pre_suf(x,1);
splay(pre,0),splay(suf,pre);
int now = tr[suf].son[0];
if (tr[now].cnt > 1) --tr[now].cnt,splay(now,0);
else tr[suf].son[0] = 0;
}
int main()
{
scanf("%d",&n);
insert(-INF,rt),insert(INF,rt);
while (n--)
{
scanf("%d%d",&opt,&x);
switch (opt)
{
case 1:insert(x,rt); break;
case 2:del(x); break;
case 3:printf("%d\n",Rank(x)); break;
case 4:printf("%d\n",kth(x + 1)); break;
case 5:printf("%d\n",tr[pre_suf(x,0)].val); break;
case 6:printf("%d\n",tr[pre_suf(x,1)].val); break;
}
}
return 0;
}