前言
原本以为 T r e a p Treap Treap已经很难了,学习了 S p l a y Splay Splay,我才知道,没有最难,只有更难。(强烈建议先去学一学 T r e a p Treap Treap再来看这篇博客)
简介
S p l a y Splay Splay是平衡树中的一种,除了平衡树所共有的作用之外,它还可以维护区间翻转,这也是它能成为 L C T LCT LCT辅助树的原因(不过 L C T LCT LCT并不是这篇博客所探讨的内容)。
因此,这篇博客将分为三个部分,第一个部分讲讲 S p l a y Splay Splay与其他平衡树的不同之处,另外两个部分则分别借助两道模板题,来讲讲 S p l a y Splay Splay两方面的作用。
S p l a y Splay Splay与其他平衡树的不同之处
s p l a y splay splay这个单词在百度翻译上的解释是张开。而在这里,则是指伸展(其实伸展与张开差不多)。
S p l a y Splay Splay最重要的操作自然就是伸展操作了。
而伸展操作,则是建立于类似于 T r e a p Treap Treap的 R o t a t e Rotate Rotate旋转操作上的。
R o t a t e Rotate Rotate操作在简析平衡树(二)中已经介绍过了,这里就直接贴代码了:
inline void Rotate(int x,int &k)//好吧,两个Rotate操作还是有点区别的,因为Splay还要记录每个节点的父亲,这里的Rotate是要将x节点向上旋转一位,而k则是要旋转到的目标位置
{
int fa=node[x].Father,grandpa=node[fa].Father,d=Which(x);//我们用fa记录当前节点的父亲,grandpa记录当前节点的祖父,d记录当前节点是父亲的哪一个儿子
if(fa^k) node[grandpa].Son[Which(fa)]=x;//如果当前节点的父亲不是目标位置,那么更新当前节点祖父的儿子为当前节点
else k=x;//否则说明已到达目标位置
node[x].Father=grandpa,node[fa].Son[d]=node[x].Son[d^1],node[node[x].Son[d^1]].Father=fa,node[x].Son[d^1]=fa,node[fa].Father=x,PushUp(fa),PushUp(x);//这个与Treap中的操作差不多,先将当前节点的父亲更新为当前节点的祖父,并将当前节点原先父亲这一位的儿子更新为当前节点与这个方向相反的儿子,并将当前节点原先的父亲更新为当前节点的儿子,最后记得更新节点信息
}
而 S p l a y Splay Splay的主要功能,就是通过不断地 R o t a t e Rotate Rotate,使某一节点旋转至目标位置。
我们可以对 S p l a y Splay Splay每次的旋转操作进行分类讨论:
第一种情况, f a fa fa即为目标位置,此时只要上旋 x x x即可到达目标位置。
第二种情况, x x x, f a fa fa, g r a n d p a grandpa grandpa在同一直线上,且 f a fa fa不为目标位置。此时,我们需要将 f a fa fa上旋,同时带动 x x x上旋,然后再上旋一遍x。
第三种情况, x x x, f a fa fa, g r a n d p a grandpa grandpa不在同一直线上,且 f a fa fa不为目标位置。此时,我们直接将 x x x上旋,将 x x x的父亲更新为它的祖父 g r a n d p a grandpa grandpa,然后再上旋一遍 x x x即可。
具体代码实现如下:
inline void Splay(int x,int &k)//将x一直旋转到目标位置k
{
for(int fa=node[x].Father;x^k;fa=node[x].Father)//重复循环,直至x为k
{
if(fa^k) Rotate(Which(fa)^Which(x)?x:fa,k);//如果fa不是目标位置,若x,fa,grandpa在同一直线上,我们需要将fa上旋,否则,直接将x上旋
Rotate(x,k);//归纳可以得出,不管什么情况下都要将x上旋
}
}
讲完了 S p l a y Splay Splay与其他平衡树的不同之处,后面的就很简单了。
模板1:【洛谷3369】【模板】普通平衡树
这应该是我第三次用到这个模板了&