后来加的说明:这个代码版本奇丑无比。。。。
题目详见NOI官方网站http://download.noi.cn/T/noi/noi2005A.pdf 第二题。
这道题令我调试得很痛苦。任何一个小错误即使对拍出来也很难找到,因为太繁琐了。
题目要求对一个序列进维护,但是要求添加和删除区间。显然这里线段树就不能用了,只能用splay。好像也可以用块状链表做,但是据说会非常非常麻烦。
我们这样来对一个序列用splay维护:
对于每个节点,记录它的父亲 pt,儿子 ch[0..1],子树大小(区间大小)size,权值 data,区间和 sum,左端区间连续最大和 ls,右端区间连续最大和 rs,区间连续最大和 ms。
基本的添加,删除区间和区间求和就不详细说了,但是要记住一定要update,即操作结束之后区间更新到根的路径。
对于覆盖 MAKE_SAME 操作和旋转 REVERSE 操作,我们可以类似于线段树,用标记下放来维护,并且在每次旋转的时候都下放标记。这里需要注意的是,旋转的标记应该记为原来旋转标记的not值,并且需要交换 ls 和 rs。覆盖之后,如果覆盖值为负,则ls, rs, ms 的值为覆盖值,否则为新得的 sum。
对于区间连续最大和 ms 的维护,是这道题目的重点之一:
首先,我们需要维护 ls 值和 rs 值。容易知道,ls = max(左子树的rs, 左子树的sum + data, 左子树的sum + data + 右子树的rs,(无左子树时)data + 右子树的rs), rs 也类似于此(证明从略)。
然后我们来看看 ms 值,他的维护更为复杂:ms = max(左子树的ms,右子树的ms,左子树的rs + data,右子树的ls + data,左子树的rs + 右子树的ls + data)。
为了完成以上的维护操作,我们可以通过判断子树的情况(无左右子树,只有左\右子树,左右子树都有)来维护,但是这样比较麻烦,而且如果没判断好,访问 NULL 的成员会直接让程序挂掉。
怎么办呢?我们其实可以这样:直接令一个节点为 null (小写区分于NULL),令他的 sum, size 为 0,而 ls, rs, ms为负无穷。这样做就可以不用在维护ls,rs 的时候判断是否有左右子树了,会使代码变得更清楚,而且更可以避免一些因为访问 NULL 而挂掉的错误。需要注意的是,在update, pushdown的时候要避开对 null 进行操作。
还有一个小技巧可以简省代码:在原序列