伸展树是一枚二叉树,可以维护一个数列,或者可以作为二叉搜索树,因为无论怎么旋转,它的中序遍历是不变的。

 

1、伸展操作。

Splay(x,goal):旋转结点x,使它成为结点goal的儿子。

至于旋转,本质上只有左旋和右旋。

 

2、插入。

如果作为二叉搜索树,则插入与二叉搜索树一样,最后把该结点旋转到根。

如果用于维护一个数列,要求在x位置之前插入val(val可以为一个数字或一个序列),则将第x-1个结点旋转到根,将第x个结点旋转到第x-1个结点的儿子,那么根据中序遍历,val应插入在第x个结点的左儿子。

 

3、删除。

与插入同理。

 

4、Select(k)。

在二叉搜索树中,对每个结点维护一个num值,如果x左儿子的num+1=k,x就是第k小的数。

在维护序列中,显然就是第k个数。

 

5、翻转。

区间:将第a-1个数旋转到根,将第b+1个数旋转到a-1的儿子,那么b+1的左子树就是区间[a,b]。

在b+1的左儿子标记翻转,像线段树一样延迟标记,延迟标记往下推的时候交换左右儿子。

 

6、循环平移。

序列:1、2、3  --循环左移一位后->   2、3、1。

本质上就是两个区间的交换。

 

坑爹的地方:

1、下标为0的结点作为边界,它的pre、next值被乱搞没关系,但是它维护的数值(比如 num,max,min...)不能影响到要维护的数列。

2、区间翻转,则左右儿子交换,可能左右儿子维护的值也需要交换。

3、不断的删除和插入一段序列时,可能会爆内存,删除的时候只好把删除的结点压到栈里,循环利用。

4、区间操作时,可以在序列的两端加入两个边界结点1和n+2。避免讨论,但它维护的数值不能影响到要维护的数列。

 

【HYSBZ】1588 营业额统计

数据有问题,用Splay实现很裸的二叉搜索树。

 

【POJ】3481 Double Queue

依然是Splay实现二叉搜索树。

 

【HYSBZ】1503 郁闷的出纳员

依然是Splay的操作。如果在平衡树中维护一段区间,蛋就碎了。

I命令:立刻离开不算入离开公司的员工的总数。

可以把数据分成两部分,在Splay中的部分,和待加入平衡树的部分。

把Splay中的数据同时加上val(val可能为负),相当于把待加入Splay的部分同时减去val,因为两个部分数据的相对值不变。

S命令:删除Splay中小于相对标准的子树。

 

【HDU】1754 I Hate It

Splay维护区间最值。

线段树是一个区间平分为两个子区间。

Splay根据中序遍历,把一个区间分为左、中、右。

维护上大同小异。

 

【HDU】3487 Play with Chain

删除、插入、翻转,之前扯淡过了。

 

【HYSBZ】1500 维修数列

修改、求和、最大连续子序列和、都和线段树一样,翻转的时候左右连续的最大和要交换。

 

【POJ】3580 SuperMemo

循环移动T次,T>0表示左移,T<0表示右移。取模以后都转换成左移即可。

 

【HDU】2475 Box

很容易想到,5在1内可以把5作为1的儿子,那么可以得到一片森林,而且儿子可能很多。

那么可以用动态树,其实用Splay也可以做到。

那么建树完DFS一遍,可以得到一个或者多个括号序列。比如2在1内,3在1内,4在3内就是1 2 2 3 4 4 3 1。

接下来,可以用Splay维护多个序列。

询问root的时候,就是该序列的最左端的值。

移动的时候,就是把一段序列移到另一段序列中。

 

【HDU】3726 Graph and Queries

给出一个无向图,图中每个顶点都有一个权值:

1、删除一条边;

2、询问与顶点x连通的顶点中,第k大的值。

3、改变某个顶点的权值。

输出所有询问的平均值。

膜拜ghnjk大神的做法和他的AVL……

由于删一条边不能确定该图是否边分成两个连通分量,但是加入一条边可以判断是否把两个连通分量连通、或者还是一个连通分量(并查集)。

所以离线读入所有询问,把每个顶点的权值存入该顶点的邻接表。

初始时,每个顶点都是一个单独的Splay。

当合并两个集合时,将两枚Splay合并,由于两枚Splay的值不会总是严格大于对方,所以只好把小的Splay的每个顶点插入到大的Splay中。

posted on 2012-08-20 23:02  DrunBee  阅读(3428)  评论(0编辑  收藏  举报