描述
分析
本题就是一个普通的通过打标记实现区间翻转的
splay
题目. 之前发过翻转卡片和这个题很像. 这里主要是想仔细分析一下标记的实现以及一些问题. 为后面的维护数列做一些准备工作.
- 首先明确, 为什么可以通过标记的方式记录翻转信息.
splay 的一棵子树代表的就是一个连续的区间, 因为splay是BST, 满足结点o的左儿子(lc) <= 根节点(o) <= 右儿子(rc). 并且如果o是其父结点的左儿子, rc一定比o的父节点要小; 如果o是其父结点的右儿子, lc一定又比o的父节点要大. 这样就保证了区间是连续的, 而且这个区间的序列就是这棵子树的先序遍历的结果.
- 既然子树是一个封闭的区间了, 对其打上翻转标记后就可以表示区间已经被翻转了. 那么如何标记下传呢? 首先根结点的左右子树应该交换一下, 也就是以根结点o为分界点, 比o小的lc交换到比o大的rc上, 比o大的rc交换到比o小的lc上. 接着以左右子结点为根节点分别将标记下传到左右两棵子树上… 层层下传, 最终实现整个区间的翻转. 这就是分治的思想. 当然实现中需要时才将标记下传一步.
- 最后一个问题, 何时进行标记下传? 标记下传后如何维护其父结点以及祖先的值?
这里还要分两种情况, 第一种是自底向上的splay, 这种需要在find()
时把遇到的所有结点进行标记下传,find()
会经过到达目标结点的所有结点. 另一种是自顶向下的splay, 需要在splay
操作时把根结点和目标所在的子结点标记下传. 因为既要找到目标结点在根结点的方向(左子树还是右子树), 又要找到目标结点在子结点的方向.
而标记下传后需不需要维护祖先结点的值呢? 其实不需要过多维护, 因为splay操作自带维护操作——旋转操作里就有.
代码