初识splay
学splay有一段时间了,一直没写......
本题是splay模板题,维护一个1~n的序列,支持区间翻转(比如1 2 3 4 5 6变成1 2 3 6 5 4),最后输出结果序列。
模板题嘛......主要了解一下splay的基本操作QwQ
1.基本概念
splay是一种二叉搜索树,节点的权值满足lson<p<rson,故可以像其他二叉搜索树一样在树上二分查找某数排名,排名为k的数,以及前驱后继等。
普通的二叉搜索树在面对特殊数据时树的深度会从log n退化成接近n(退化成链),这样操作的时间复杂度会从O(log n)退化成O(n),影响效率。
splay通过旋转维持树的平衡。这个操作后面会提到。
2.核心操作
splay的核心操作是splay(一脸懵逼),splay(x,y)意为通过一系列旋转,将点x旋转到点y下面,使x成为y的儿子。
每次旋转通过rotate函数实现:
1 void rotate(int p) 2 { 3 int fa=f[p]; 4 bool k=id(p); 5 s[fa][k]=s[p][!k]; 6 s[p][!k]=fa; 7 s[f[fa]][id(fa)]=p; 8 f[p]=f[fa]; 9 f[s[fa][k]]=fa; 10 f[fa]=p; 11 refresh(fa); 12 refresh(p); 13 }
rotate的时候严格满足splay二叉搜索树的性质:lson<p<rson。
将p提到fa的位置,根据大小关系决定fa是作为p的左儿子还是右儿子,这样实际上是fa挤掉了p原先的某个儿子,而p转上去,让出了fa的一个儿子的位置。
所以最后让那个被fa挤掉的p的孤儿作为fa的某个儿子,填到空缺的地方去(原来p的位置)。
至于splay的实现方法...有两种:单旋和双旋。
单旋即无脑地一直转,直到把x转到y下面。
1 void splay(int p,int g) // 单旋 2 { 3 while(f[p]!=g)rotate(p); 4 if(!g)root=p; 5 }
比起单旋,双旋能更好的维护splay的平衡。
1 void splay(int p,int g) // 双旋 2 { 3 while(f[p]!=g) 4 { 5 int fa=f[p]; 6 if(f[fa]==g) 7 { 8 rotate(p); 9 break; 10 } 11 if(id(p)^id(fa))rotate(p); 12 else rotate(fa); 13 rotate(p); 14 } 15 if(!g)root=p; 16 }
利用splay操作,我们就可以用这棵树实现很多其它平衡树实现不了的功能。
3.元素的插入与删除
(未完待续)