11.20 CW 模拟赛 T3.货物分组
算法
考虑 \(\rm{dp}\)
令 \(f_{i, j}\) 表示前 \(i\) 个数中, 分成 \(m\) 组的最小花费
关于转移, 我们有
其中 \(\rm{Sum}\) 可以前缀和预处理, \(\max, \min\) 可以 \(\rm{ST}\) 表预处理
这样转移是 \(\mathcal{O} (n ^ 3)\) 的, 考虑优化
这里是一个 \(\rm{trick}\) , 即费用提前计算, 题面中花费 \(i \times w_{sum}\) 转化成每一次的花费都是 $ \rm{Sum}(i + 1, n)$
将 \(\rm{dp}\) 柿子转化成 \(dp_i = \min \left[dp_j + Sum(j + 1, n) + \max(j + 1, i) - \min(j + 1, i))\right]\)
复杂度 \(\mathcal{O}(n ^ 2)\) , 考虑优化转移
对于 \(Sum(j + 1, n)\) 是定值, 我们考虑将其提出
方程转化成 \(dp_i = Sum(j + 1, n) + \min \left[dp_j + \max(j + 1, i) - \min(j + 1, i))\right]\)
考虑令 \(g_j = dp_j + \max(j + 1, i) - \min(j + 1, i))\) , 维护 \(g_j\) 最小值
这是可以维护的吗?
考虑线段树维护 \(g\) ,
对于每一次更新 \(dp_i\)
我们可以在单调栈中找到每一段区间最大值小于 \(a_i\) 最小的 \(j\) , 显然的, \(j \sim i\) 这一段中, 区间最大值都需要更新
容易发现, 这就是单调栈所维护的基本信息, 本质上是找当前节点左侧第一个比自己大的元素, 用一个单调递减栈即可, 特别的, 对于这两个单调栈, 维护的都是前缀最值
于是我们只需要线段树维护区间最值 and 区间加法
复杂度 \(O(n \log n)\) , 常数稍大
代码
第一次写单调栈维护
代码大致分为几个板块:
- 维护区间最值, 有区间加法功能的线段树
- 单调递减栈, 维护区间最大值小于 \(a_i\) 的左端点, 单调递增栈, 维护区间最小值大于 \(a_i\) 的左端点
注意单调栈事实上维护的是几段, 每一段线段树上更新的值都不同, 不能一次弄完
这几天状态真的不好, 但这题也是真的搞懂了, 懒得再去打代码和调了
总结
费用提前计算是一个巧妙的 \(\rm{trick}\)
注意 \(\rm{dp}\) 不能仅仅简单的省略掉一维, 很有可能会导致正确性出现问题, 这个多练练应该可以解决
对于神秘转移式子, 考虑多来几种数据结构维护
单调栈/队列 是常见的数据结构维护
神秘维护, 诗人握持

浙公网安备 33010602011771号