第一步:题意转化
做法一:线性规划对偶
这是本题唯一一个可能用到的所谓 “高深知识点”,“可能” 也就意味着本题存在的做法二。
做法二:贪心
为了方便叙述,我们先将题目表述为:
- 我们每次让一个 长的区间中所有的 消去 1,使得最终所有的 减为 0,求最少的消去次数。
容易发现消去的顺序与答案无关,换句话说,不管我们是从左往右消还是从中间开始消,我们只不过是把消的顺序调换了,因此我们从左往右,每次将消去区间的左端点放在左边第一个 不为 0 的位置,一定有一种方案是最优解。
如此以来,每次消去时左端点的选择已经固定,就是从左往右第一个 不为 0 的位置。那么,右端点应该如何选择?
- 先考虑前 j () 个位置构成不减序列的情况。
这种情况下我们只需要直接将前 m 个位置全部消去 1 即可,因为由于这是不减序列,我们贪心地希望每次能够消去的东西尽量多,而在序列一直为不减序列的情况下,消去区间的长度始终为 m。相信这一部分读者很好理解。
- 按照这种规律一直消到前 j' () 个位置构成不减序列的情况。
我们发现,在 和 之间存在一个 gap,这个 gap 必须要 次右端点在 的操作才能消去。那么,先消去这个 gap 显然是不劣的。
除此之外,我们需要充分利用这 次操作,即使得这几次操作尽量多地消去。否则,如果我们先进行右端点超过 的操作,将会使得 前可消去的部分变少,就可能使得消 gap 操作消去的东西变少了,最终将会导致答案偏大。
因此结论就是:
我们仍然只需要让前 j' 个位置全部消去 1,直到 gap 消失。
从上述做法,我们知道仅有当 时消去的区间长度小于 m。我们又知道,这个 gap 是必然要被消去的,问题只不过是消去的左端点在哪。
不妨直接将 消去至 gap 消失。即,令 ,我们使 。
这样操作之后,我们相当于把问题转换为:
- 至少要多少次操作可以将每一个数字都减到 ≤0(不必正好等于 0)。
这是为什么呢?
首先,对于一个不减数列,在贪心地消去时,我们不用保证每个位置都被刚好减为 0,而是可以减为负数,这样做和原题的答案是相同的,这很显然。
那么,我们从左往右依次考虑每个 gap,第一个 gap 之前是个不减数列。
如果我们提前消去这个 gap,那么这对于右端点在 gap 之前的消去所产生的答案不变。
那么,由于消去的区间长度为 m,如果我们提前消去这个 gap,则当右端点移动到(或经过)原 gap 所在位置时,一定满足一个条件:
该位置前(m 个位置以内)一定是一个不减序列。
那么,我们就把问题转移到了第二个 gap,此时第二个 gap 之前已经是不减序列,依此类推。
另外,这种做法实际上还有一个问题:
既然我们提前消去,那么最终答案显然也要加上区间内提前消去的次数,并且我们的询问是在提前消去过后的数列上进行。
但是,对于我们询问的区间,在这个区间之后的 gap 提前消去的区间可能覆盖到询问区间内。对于这一部分 gap,如果我们也提前消去,那么将会导致答案不正确,因为这些 gap 根本不在区间内。如何解决这一问题?
事实上,有一个非常聪明和简洁的办法:当我们消到区间内最后 m 个位置时,此时的序列一定是不减的,因此最后 m 个位置的答案就是区间最后一个数的原值。而不在 gap 内的提前消去最多影响到区间的后 m 个位置。于是,非常凑巧地,我们只需要对提前消去后的数列,求出 的答案,再加上 的原值以及 内所有 gap 的提前消去次数即可。
现在考虑如何求出提前消去之后的数列上某个区间的答案。
假定对于这个数列,我们消去的策略类似,依然是从左到右每次把左端点放在第一个 的位置上,并且不需要让 刚好减为 0。那么,可以视为区间长度始终为 m。
那么,当左端点移动到 i 时,设 为此时的 。那么, 就是左端点在 i 的消去次数。
使得 减为 的则是所有距离 m 以内的消去次数,即 之和。于是我们写出式子
令
可得
即
而所求答案即为 。
我们发现,这个式子与以下问题等价:
- 在长为 n 的数列上,每个长度为 m 的区间内最多有一个数字被选中,求最终选中的数字之和的最大值。
下面考虑如何解决这一等价问题。
主干解法 - 分类分治
我们对于原序列分段,每段长度为 m,共 段。然后,将分段后的序列排成一个矩阵,矩阵的每行为一个整块,即每行 m 个元素,共 行。更具体地,记矩阵的第 i 行第 j 列为 对应原序列的第 个位置。下面进行分类讨论。
-
Case 1:
对整个序列进行二分。具体地,在整个序列中间画一条竖线,也就是在整个矩阵的中间位置画一条横线,将整个矩阵从中间截断。对于跨越了这一分界线的询问区间,其必然存在一个跨越该分界线的长度为 (m−1) 的子区间,使得该子区间内没有数被选中。枚举这 m 个子区间,并且对于每个子区间的左端点,利用递推式预处理出其向左到任意点的答案;对于右端点,预处理出其到右边任意点的答案。于是对于跨越该分界线的询问 ,设某个子区间为 ,将左右答案 和 求和,再对每个子区间对应的答案取 ,就是该询问的答案。从而我们能够 O(m) 算出所有跨越区间中点的询问的答案。
这样的话我们需要预处理的时间复杂度为 ,初始状态为 。又因为 ,故 。这样的话我们就把问题化归为 的情况了(因为最后不能求解的区间的长度 )。
-
Case 2:
下面进行二次分类讨论。
- 矩阵中存在一整行被询问区间包含,且这一行内没有任何数被选择。
枚举这一行是哪行,设这一行对应原序列的 。容易发现这个整段的左右互不影响,答案为 。因此,我们对每两行之间的分界点,向左向右预处理出到任意点的答案。预处理时间复杂度为 。
- 矩阵中任意一个被询问区间包含的行都存在一个数被选择。
容易发现对于相邻两个被选择的数,它们在矩阵上的位置一定是后一个数在前一个数的右下方向。然后,将所有被选择的数依次连成一条折线。
我们在矩阵中间划一条竖线,它将每行分为长度为 , 的两段,将第 i 行的后一段与第 行的前一段拼接,又能得到若干个长度为 m 的段,我们再次套用 1. 中的解法,相当于解决了折线越过我们所划的竖线的情况。
接下来需要解决折线不越过竖线的情况,那么如果在折线一边选了数,另一边就不能选数,在解决竖线一侧的问题时,另一侧的数完全无用。
若将一侧的元素顺次拼接,得到一个长度为原来的一半的序列,并认为 m 也随着矩阵宽度变化为原来的一半,解决一侧的问题相当于解决规模更小的原问题。
于是我们得到了两个规模更小的与原问题题意一致的子问题,n,m 均为原来的一半。若一个询问的最优解选择的第一个数与 l 或选择的最后一个数与 r 不在竖线的一侧,这种情况在之前套用 1. 中算法的时候已经计算过了,不需要递归,因此每个询问至多只会被递归到一个子问题中。
时间复杂度分析
预处理时间复杂度为 ,。
每次询问时间复杂度为 ,。
总时间复杂度为 。
至此,我们在 的时间复杂度内解决了本题,采用离线整体二分的做法做到 O(n) 空间复杂度,即可通过本题。