二叉搜索树(Splay、fhq Treap)

Splay

Splay(伸展树)是一种二叉搜索树。
其复杂度为均摊\(O(n\log n)\),所以并不可以可持久化。
Splay的核心操作有两个:rotate和splay。

rotate:

rotate实现把一个节点\(x\)转到它的父亲\(y\)的位置。
假设\(x\)\(y\)的左儿子。
那么旋转完之后,\(y\)就会变成\(x\)的右儿子。
那么\(x\)原来的右儿子的地方就被占了,我们就把它放到\(y\)的左儿子。
实际上就是把\(x\)\(x\)的右儿子,\(y\)相对位置转了一圈。
如果\(x\)\(y\)的右儿子那么就反着来。写的时候可以通过一些小trick浓缩成一种方式。
注意修改的顺序!

void rotate(int x)
{
    int y=fa[x],z=fa[y],k=ch[y][1]==x;
    fa[x]=z,ch[z][ch[z][1]==y]=x,fa[ch[x][!k]]=y,ch[y][k]=ch[x][!k],fa[y]=x,ch[x][!k]=y,pushup(y),pushup(x);
}

pushup:

上传信息,比如区间和、子树大小...保险的办法是每次修改完之后都pushup一下。

splay:

splay实现把一个节点\(x\)转到\(p\)的儿子的位置。
一个很简单的想法是直接rotate(这样的Splay称为单旋Splay,或者叫Spaly)。
但是这样的复杂度在某些情况下会有问题。
\(x\)是父亲的左/右儿子,而\(x\)的父亲也是\(x\)的爷爷的左/右儿子的时候,我们先rotate\(x\)的父亲再rotate\(x\),这样可以保证Splay的复杂度正确。
为了保证Splay的复杂度正确,我们需要在所有操作的末尾都做一次splay。
(上面这段话记得区分大小写)
证明我不会,上网看吧。
如果旋到根了记得修改一下根。

void splay(int x,int p)
{
    for(int y,z;(y=fa[x])^p;rotate(x)) if((z=fa[y])^p) (ch[y][0]==x)^(ch[z][0]==y)? rotate(x):rotate(y);
    if(!p) root=x;
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/11972013.html