[splay模板]洛谷P3391 文艺平衡树

初识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.元素的插入与删除

(未完待续)

猜你喜欢

转载自www.cnblogs.com/eternhope/p/9474709.html