[CF1648D]Serious Business 题解
[CF1648D]Serious Business 题解
首先容易想到一个 \(dp\) 转移式子:
其中 \(dp_i\) 表示到第 \(2\) 行的第 \(i\) 列处移动至第 \(3\) 行的最大价值, \(cost_{j,i}\) 表示第 \(2\) 行 \(j\) 到 \(i\) 联通的最小代价, \(s_{i}\) 为第 \(i\) 行的前缀和数组。
其余部分都是可以预处理的,只有 \(cost_{j,i}\) 这一块儿是没法快速求出的,其实可以考虑枚举哪一条线段包含了 \(i\) 然后进行转移,大概形式为:
其中 \(val_j\) 表示选择的最靠右的线段为 \(j\) 时,并且当前没有走到 \(i\) 的最大价值。
而 \(val_j\) 其实有两种情况:第一种是只使用了 \(j\) 这一个线段;另一种是使用了超过一条线段,并且所有选择的线段的并也是一条完整的线段。
两种情况的 \(dp\) 式子为:
先看第一个式子。因为是求区间 \(\max\) ,所以说可以考虑用一下线段树去优化转移。
因为是 \(\max\) 套 \(\max\) ,所以说我们线段树去维护转移点时也可以分两段去维护。第一个维护的值是区间的 \(\min k_j\) ,第 \(j\) 条线段对于 \(i\) 点转移的额外贡献为 \(k_j\) ,影响区间为 \([l_j,r_j]\) ,所以可以写一个区间取 \(\min\) 。
然后看内层枚举 \(t\) 如何维护。考虑 \(t\) 能作为哪些 \(i\) 的决策点,显然是 \(i\in[t,n]\) 的点,所以说可以再写一个区间取 \(\max\) ,每次在一个后缀插入 \(s_{1,t}-s_{2,t-1}\) 。
有了这两个值的维护,也就可以同时求出第一个式子的转移式子了。
但是有两个区间修改,还要动态维护这两个值的差的最大值,不是很好维护,这里有一个小 \(trick\) 可以用一下:
因为考虑到每一次 \(t\) 的值的修改都是一个后缀,而我们插入线段没有必要一次性全插进去,可以当进行 \(dp_i\) 的转移时再把所有 \(l_j\le i\) 的线段插入线段树,所以说其实可以把线段的修改由 区间修改 转变为 前缀修改 ,所以说其实可以再进一步抽象,把 前缀修改 变为在 \(r_j\) 处的单点修改,并且查询由 单点查询 变为 后缀查询 ,可以发现,这样正确性是有保障的(其实前缀变成单点,然后查询再把后缀查了,就是相当于把原来能够干预到 \(i\) 的所有线段都计算了一遍),而这样修改只有一个区间修改,就会非常好操作。
现在说一下第二个式子,这个就比较简单了,可以维护一个 \(set\) ,维护当前所有 \(l_j\le i\le r_j\) 的线段的 \(dp_{l_j-1}-k_j-s_{2,l_j-1}\) 的值,然后当 \(i>r_j\) 时就把 \(j\) 对应的值删掉即可。
下面给出 代码 。