P14665 bonus 细谈
yonghu10010 · · 算法·理论
原题
这里提出对于
关键观察
区间加/减可以转换成环上补集的区间减/加。
证明:考虑数字之间的相对大小,如果一段被削减了,以它为参考系,其它段就抬升了。这是等价的。
在这里,我们将区间减转化成环上区间加。
那么自然,区间加不能涉及到最大值,否则这一次是无意义的。如果某一次操作涉及了最大值,我们完全可以让它缩短一点“吐出”最大值,因为这样得到的结果一定不劣(如果包含最大值,结果就会是
既然如此,我们的目标就变为:在
并且每次操作也被最大值天然隔断了,此时就可以以任意一个最大值为断点断环成链,换回序列问题,并且区间减一的操作已经被转化掉了。
最大值记作
贪心构建
现在考虑
它假定了:“每次操作必然能将全体最大/小值进行一次削减/抬升”,而这在最大最小值交替出现时就出现了错误——无法一次性在不影响另一个最值的前提下整体修改当前最值。
一组数据:
4 1
1 2 1 2
答案是
如何改正原思路呢?那我们可以考虑不去假定“每次操作必然能将全体最小值进行一次抬升”,而是去想:
“至少需要多少次操作才能让全体最小值进行一次抬升?”
有了!维护这样的操作段即可。不过我们不关心到底是哪些段,因此只用考虑维护段数。
性质挖掘
先直接将值相同的连续段挑出来,它们可以当成一个单点做,缩一块即可。
根据结论以及一又点点观察,我们又知道:
每次操作肯定提升了一个不包含最大值且包含最小值的极长段。
这是很显然的一点,既然不涉及最大值,那么自然越长越好。
长什么样子?考虑一个已经被转换后的序列:
xo---x--x-o-o--x-xo---
x 表示最大值,o 表示最小值,- 表示中间值。
那么操作段 n,就长这样:
xnnnnx--xnnnnnnx-xnnnn
共
那么究竟如何维护呢?接下来将会涉及许多转化,跟上咯!
我们需要解决两个问题:
- 如何得到新最小值的位置,以及它对段数的影响?
- 如何得到新最大值的位置,以及它对段数的影响?
问题 1
为什么会有这个问题?考虑当我们将现有最小值从
例子:
xnnnnx--xnnnnnnx-xnnnn
变为
xnnnnxo-xnnnnnnx-xnnnn
变为
xnnnnxnnxnnnnnnx-xnnnn
这样就会增加段数。
原最小值肯定还是新的最小值,因此不用考虑删除。
我们发现一个性质:每当多出一个最小值,其带来的影响就是一连串的,它会导致从它开始,向左右扩展,直到碰到边界或者最大值为止的位置集合全变成操作段的一部分。
我们维护一个 vis[] 数组,看看某个新的最小值是否导致新段的产生即可。
问题 2
为什么会有这个问题?考虑我们操作某段时,中间可能会有“间谍”——等于
我们需要迅速挑出这些间谍。
从头来看,每个值从原值加到
我们可以维护一个
每当我们更新操作段时,就将那些新加入的位置加入到
如何维护?对于初始被最小值加入的位置来说,它们的特征值自然是这个位置的原值
在枚举到
不会出现负特征值,因为所有值迟早都会变成
小示例
3 1 2 1 3 2 3
操作段为
3 2 3 2 3 2 3
发现:位置
实现要点
我们将连续段挑出来后,按照值建桶。
将初始最小值扩展一遍,
从小到大枚举最小值
顺序为:
- 先 check 旧值能否提升得到这个新值
h (m \ge cnt? ),如果不能就退出,如果能,就将m 减去cnt ,d 减一,并进入2 ; - 然后扫一遍
h 的值域桶,看这些最小值是否已经被覆盖过,如果没有覆盖过,则进入3 ;随后扫一遍d 的桶dl_{d} ,对于每个在这里被扫到的位置,进入4 ; - 向左向右扩展,直到撞到已经变成最大值的位置为止。对于每个新加入的位置
pos ,将其加入dl_{a_{pos}-(h-P)} ; - 看看其左右是否有已经变为最大值的标记(用一个布尔数组存即可),然后标记当前位置。如果左右都没有最大值,说明这是一个存在于操作段内部的隔断,
cnt+1 ;否则,如果恰好有一个最大值,说明这在边界,cnt 不变;否则,说明我们把一个最小值变成了最大值,程序就该结束了。
对于步骤
每个位置最多被扩展一次、标记最大值一次、压入
总时间复杂度
这样应该有蓝了吧