[CF1648D]Serious Business 题解

[CF1648D]Serious Business 题解

首先容易想到一个 \(dp\) 转移式子:

\[dp_{i}=\max_{j\le i} s_{1,j}-cost_{j,i}-s_{2,j-1}+s_{2,i}+s_{3,n}-s_{3,i-1} \]

其中 \(dp_i\) 表示到第 \(2\) 行的第 \(i\) 列处移动至第 \(3\) 行的最大价值, \(cost_{j,i}\) 表示第 \(2\)\(j\)\(i\) 联通的最小代价, \(s_{i}\) 为第 \(i\) 行的前缀和数组。

其余部分都是可以预处理的,只有 \(cost_{j,i}\) 这一块儿是没法快速求出的,其实可以考虑枚举哪一条线段包含了 \(i\) 然后进行转移,大概形式为:

\[dp_{i}=\max_{l_j\le i \le r_j}val_{j}+s_{2,i}+s_{3,n}-s_{3,i-1} \]

其中 \(val_j\) 表示选择的最靠右的线段为 \(j\) 时,并且当前没有走到 \(i\) 的最大价值。

\(val_j\) 其实有两种情况:第一种是只使用了 \(j\) 这一个线段;另一种是使用了超过一条线段,并且所有选择的线段的并也是一条完整的线段。

两种情况的 \(dp\) 式子为:

\[\left\{ \begin{matrix} &dp_i&=&\max_{l_j\le i\le r_j}&\{\max_{l_j\le t\le i}s_{1,t}-s_{2,t-1}\}-k_j+s_{2,i}+s_{3,n}-s_{3,i-1}\\\\ &dp_{i}&=&\max_{l_j\le i\le r_j}&dp_{l_j-1}-k_j-s_{2,l_j-1}+s_{2,i}+s_{3,n}-s_{3,i-1} \end{matrix} \right. \]

先看第一个式子。因为是求区间 \(\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\) 对应的值删掉即可。

下面给出 代码

posted @ 2023-03-06 23:00  _zyc  阅读(41)  评论(0编辑  收藏  举报