关于吉利线段树

引言

这玩意儿又称(Segment Tree Beats) 。由吉老师、(Picks)、美国队长三位知名毒瘤发明。
我的精神受到了污染......

线段树简介

线段树什么的太难了,不是很会啊.....
线段树最强的地方在于,如果标记支持合并,那么我们就能够快速进行区间修改了。

一类特殊标记

维护这样一个标记((a,b)) ,表示(x o 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' o hmx)(mn + mnt' o hmn)(t + mxt' o mxt)(t + mnt' o mnt)
然后再把当前值改为真实值:(t' o t)(t' + mx o mx)(t' + mn o 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 ; 代码

原文地址:https://www.cnblogs.com/GuessYCB/p/10349422.html