例题:
1.bzoj 1588
题意:每读入一个数,在前面输入的数中找到一个与该数相差最小的一个。把所有的差值加起来。
思路:三种操作:插入,求前驱,后驱。
代码
2.hdu 3487
题意:两种操作:
操作1. CUT a b c 表示把数列中第 a 个到第 b 个从原数列中删除得到一个新数列,并将它添加到新数列中第 c 个数的后面
操作2. FLIP a b 表示把数列中第 a 个数到第 b 个数翻转
思路:
对于操作1. CUT a b c” ,只需要将 a-1 旋转到根,b+1旋转成 a-1 的子树,那么 [a, b] 之间的树变成了 b+1 的左子树,将整个子树从原树中删去,并把 b+1 旋转到根。之后把 c 旋转到根,c+1 旋转成 c 的子树,再把刚才删掉的子树添加到 c+1 的左子树上(没添加之前,c+1 的左子树必然为空)
对于“操作2. FLIP a b”,只需要给树中每个节点一个 rev 标记表示是否需要翻转以该节点为根的子树中序遍历所得的数列,和线段树差不多的使用懒操作就行了
还有的就是细节问题,什么时候 pushDown,什么时候 pushUp,写代码的时候一定要想清楚
3.hdu 1890
题意: 给了N个数的一个序列,现在要把这n个数变成完全从小到大排列的N个数,那么如何变呢?每次在当前位置找后面所有的最小的数的位置,然后把当前位置和找到的这个位置的这个区间的数反转过来。最后要输出每个数在第i次操作之前的第i个数的位置。
思路: Splay做法比较显然。对区间有反转,同样用懒惰标记。依次考虑第K大的,将其旋转至根,左子树的数量便是需要反转的,之后把根删除即可。但是考虑到这道题没有区间询问的话,那么久没有必要建两个虚拟根节点出来了,也可以减少一些代码。
本题的话每个结点序号就是最开始的位置。sp树中第i个结点的序号就是第i个数的初始位置。所以对每个数排序,记录初始位置,就可以在树中直接找到要操作的结点。