引言
这玩意儿又称\(Segment\ Tree\ Beats\) 。由吉老师、\(Picks\)、美国队长三位知名毒瘤发明。
我的精神受到了污染......
线段树简介
线段树什么的太难了,不是很会啊.....
线段树最强的地方在于,如果标记支持合并,那么我们就能够快速进行区间修改了。
一类特殊标记
维护这样一个标记\((a,b)\) ,表示\(x\to max(x + a , b)\) 。
显然标记可合并:\((a,b) + (c,d) = (a + c , max(b + c , d))\) 。
这个标记可以同时处理区间加法、区间覆盖,对应为\((add,-inf)\)、\((-inf , cov)\) 。
这个标记还能取\(max\):\(max((a,b) , (c,d)) = (max(a,c) , max(b , d))\)
由于该分段函数取上平面交后段数不变,所以支持维护历史最大值,可以说是相当好用了。
然而这个东西在解决历史最小值,区间取\(min\)等一系列问题上无能为力,所以我们需要另辟蹊径。
一类历史最大、最小值问题
设当前最大值为\(mx\)、最小值为\(mn\)、修改增量标记为\(t\)。
设历史最大值为\(hmx\),历史最小值为\(hmn\)。
直接维护不好维护,我们考虑维护历史最大标记\(mxt\)、历史最小标记\(mnt\)。
可以理解为,这个历史最大标记\(mxt\)是修改标记\(t\)的一个最大前缀和,历史最小标记\(mnt\)类似。
那么下放就很简单了。
先用新的前缀和更新所有历史最优值:
\(mx + mxt' \to hmx\)、\(mn + mnt'\to hmn\)、\(t + mxt' \to mxt\)、\(t + mnt'\to mnt\) 。
然后再把当前值改为真实值:\(t'\to t\)、\(t' + mx \to mx\)、\(t' + mn\to mn\) 。
当然这些标记不一定要是加法标记。
只要是支持合并,并且能够定义大小关系的标记,我们都可以这么做。
例题:
[UOJ164]【清华集训2015】V ; 代码
一类趋近问题
当修改操作逐渐使得元素趋近于相同时,往往就可以搞事情。
目前已知的这类操作:
- 区间开根 + 加法 + 求和
- 区间或 + 与 + 求和
- 区间除法 + 加法 + 求和
解决方案就是:若该修改对当前区间影响相同则打标记,否则暴力递归处理。
比如,对于开根、除法操作,记录区间最小、最大值。
对于区间或 + 区间与操作,记录区间内每一位是否相同(用位运算即可所有位一起实现)。
例题:
[BZOJ5312]冒险 ; 代码
[UOJ228] 基础数据结构练习题 ; 代码
一类区间最值问题
势能分析之类的略过,反正我口胡的势能分析连我自己都不知道对不对。
以区间取\(max\)为例。
维护区间最小值\(mn\)、区间次小值\(se\)。
对于区间对\(x\)取\(max\)操作,首先定位到\(log\)个线段树结点,然后从它们开始\(dfs\)。
设当前点为\(u\)。
若\(x \leq mn_u\) ,显然该操作对区间无影响,\(return\) 。
若\(mn_u < x < se_u\),我们维护最小值修改标记\(t\),把最小值改为\(x\)即可。
若\(se_u \leq x\) ,暴力递归。
显然加法操作可以直接维护,额外维护一个标记\(d\)表示最小值之外的其他数的修改量。
注意一点:\(pushdown\)时,若\(se_u = inf\) , 那么\(se_u\) 不能被修改,因为我们要保证势能分析时关键点的数量。
同时\(pushdown\)的时候,我们要讨论儿子的每个值对应哪个标记,进行对应修改。
区间取\(min\)是一样的,维护区间最大值\(mx\),区间次大值\(se\)即可。
需要注意的一点:
当我们需要同时支持区间\(max\)、区间\(min\)的时候,取\(max\)时要考虑它对最大值的影响,取\(min\)时要考虑它对最小值的影响。
我们的加法标记要分为三类:\(mn.t\)、\(mx.t\)、\(md.t\),分别对应区间最大值增量、区间最小值增量,非最大最小值增量。
一样的\(pushdown\)时要考虑儿子每一个值对应的修改应该是哪一个,然后用其属于的修改标记进行修改。
例题
[BZOJ4695] 最假女选手 ; 代码
[BZOJ4355] Play-with-sequence ; 代码
当所有问题混到一起?
线段树各种操作大融合。
没什么好说的,按照上面的方法一个一个实现即可。
代码必须模块化,并且是条理清晰的模块化,否则这种题是调不出来的。
例题
[UOJ169] 元旦老人与数列 ; 代码
[UOJ170] Picks loves segment tree VIII ; 代码