Loading

11.20 CW 模拟赛 T3.货物分组

算法

考虑 \(\rm{dp}\)

\(f_{i, j}\) 表示前 \(i\) 个数中, 分成 \(m\) 组的最小花费
关于转移, 我们有

\[f_{i, j} = \min(f_{k, j - 1} + j \times \rm{Sum}(k + 1, i) + \max(k + 1, i) - \min(k + 1, i)) \]

其中 \(\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}\) 不能仅仅简单的省略掉一维, 很有可能会导致正确性出现问题, 这个多练练应该可以解决

对于神秘转移式子, 考虑多来几种数据结构维护

单调栈/队列 是常见的数据结构维护

神秘维护, 诗人握持

posted @ 2024-11-21 08:34  Yorg  阅读(17)  评论(0)    收藏  举报