南外集训 2024.1.8 T3

发布时间 2024-01-08 20:51:52作者: kyEEcccccc

题意

给定一个序列 \(a\),将之划分为两个子序列,使得两个序列前缀最大值的和之和最小。

\(1\le n\le 5\times 10^5, 1\le a_i\le 10^9\)

做法

首先 DP 很容易做到平方:考虑前 \(i\) 个数,其中一个子序列当前的最大值当然是前 \(i\) 个数的最大值,记另一个序列的最大值是 \(j\),此时的最小代价记为 \(f_{i,j}\)

考虑这个 DP 的转移:对于当前数 \(a_i\) 如果它是前缀最大值,那么显然是给所有位置加 \(a_i\);如果不是,则 \(f_{i,a_i}\) 变为所有 \(j\le i\)\(f_{i-1,j}\) 的最小值再加上 \(a_i\),小于 \(i\) 的位置加上当前前缀最大值,大于 \(i\) 的位置加上那个位置的数值。这个过程显然可以用 Kinetic Tournament Tree 维护,时间复杂度有人证明是 \(\mathrm O(n\log^2 n)\),可以通过。

下面是官方题解:考虑用 set 维护一个单调栈,在整个过程中,如果前面的某个位置小于等于后面的某个位置,则把后面的位置删除。对于 \(a_i\) 是前缀最大值的情况当然好处理,考虑不是。大于 \(i\) 的位置的部分考虑用一个线段树维护每个位置再被加多少次会比下一个位置大,那么每次只要处理一下边界,然后用线段树定位要删除的点即可。前半部分的区间加可能会导致后面的某些位置重新进入单调栈,但是由于 \(a_i\) 这个位置被赋值为前面的最小值,所以在它后面的位置无法重新进入到单调栈中,所以只需要考虑 \(a_i\) 即可。时间复杂度 \(\Theta(n\log n)\)