DP 优化
持续更新中……
前缀和优化
P2513 [HAOI2009] 逆序对数列
这题不加优化也能过,难崩
考虑这个状态定义是怎么来的。倘若尝试将 \(n\) 排列的具体顺序融入状态定义会发现需要维护一个状压状的维度,数据范围太大,肯定不可做;然后注意到,因为你每次往排列里放的都是一个比原来所有数都要大的数,所以状态决策跟 \(n\) 排列的具体顺序完全无关。于是不妨定义 \(f_{i, j}\) 表示 \(i\) 的排列中,存在 \(j\) 个逆序对的方案总数。
考虑 \(i\) 的排列是 \(i - 1\) 的排列转移而来的情况。因为后加的那个数字比先前的数字都要大,所以原来的前缀和数目 \(p\) 满足以下关系式:
确定了 p 的范围之后,就有
现在的时间复杂度是 \(O(n^2 * k)\) 的,考虑到状态转移的复杂度,我们可以对其进行优化。如何优化?我们可以使用前缀和来加速状态转移。
不妨定义 \(s = \sum_{p = 0}^{j} f_{i - 1, p}\)。因为我们在对 \(f_{i, j}\) 实施转移时,求的总是一段既有区间的和;每次 \(i\) 指针往前移动时,既有区间只会增加 \(f_{i - 1, j}\) 这一个值,因此只需在每次循环中将 \(f_{i - 1, j}\) 累加进 \(s\) 中即可。
注意特判 \(\max(0, j - i + 1) = j - i + 1\) 即 \(j - i + 1 > 0\) 的情况,此时需要将 \(s\) 减去 \(f_{i - 1, j - i + 1}\) 这一部分。
记得取模。
CF383E Vowels
高维前缀和优化 DP,也称作 SOS-DP。
注意到字母种类非常少,我们可以使用状压,把每个字母压到一个二进制位上。
如果直接求交集不太可做,因此考虑正难则反,求补集。
考虑定义 \(f_{st}\) 表示以集合 \(st\) 为元音字母集合的补集时,不合法的单词数目。
一个单词是不合法的,说明所有的元音字母都不包含在里面,进而说明它包含于元音字母集合的补集。
所以我们只需要在元音字母集合的补集上跑高维前缀和枚举子集即可。
单调数据结构优化
P2569 [SCOI2010] 股票交易
对于每支股票,可以选择不动、买入或卖出,有点像一个含有三重决策的背包状问题。
不妨设 \(f_{i, j}\) 表示第 \(i\) 天,手里有 \(j\) 股股票时能赚到的最多的钱。
- 不动\[\]\[ \]\[\]\[ \]\[\]\[ \]
由于买卖股票过程中可能会亏钱,即 \(f\) 数组值存在负数,所以最开始一定要赋一个极小值(不是 \(0\))。边界显然是 \(f_{0,0} = 0\)。
考虑如何对它进行优化。显然转移 1 无法优化。
注意到转移 2 意味着每一个 \(j\) 指针的前面都挂着一个长度为 \(as_i\) 的窗口。把状态转移方程展开,因为 \(-ap_i \cdot j\) 是个定值,可以挪到 \(\max\) 的外面,所以我们的目标就是要找到这个窗口里最大的 \(f_{i - w - 1, k} + ap_i \cdot k\)。写一个滑动窗口即可。
转移 3 的优化方式和转移 2 基本相同,不过它是 \(j\) 指针的后面挂着长度为 \(bs_i\) 的窗口。这样的话,我们就需要倒序遍历 \(j\) 的值。其他没什么不同。
P6563 [SBCOI2020] 一直在你身旁

说点闲话吧。
这题被 yyy 珍爱有加,在 24N2 公众号上曾被当作文章的一部分来投稿。早些时候 yyy 曾与我推荐过这道题,称“会了这道题就掌握了区间 DP 的精髓”。
题目背景源于动漫《Clannad》。那是我前年 12 月份看的一部动漫了,当时给了我很深的印象。记得当时发了高烧,在家请了近两周的假,把两季连着追完了。说到这部动漫,又让我想起她了。果然她给我留下了很多美好的回忆呀。
好了好了,还是回归正题吧。
定义 \(f_{l, r}\) 表示已知电线长度在区间 \([l, r]\) 内时,查明电线长度所需的最小花费。
想要查明区间 \([l, r]\) 的状况,必须在其中间挑选一根电线购买,即
断开的两个子区间要取 \(\max\) 的原因在于,我们求的实际上是在最劣的情况下,最少要支出的开销(因为题目中问的是“至少”)。
现在你得到了 20 pts,接下来是优化。因为 \(\min\) 套 \(\max\) 很麻烦,所以考虑把 \(\max\) 分讨掉。进而,需要找到刚好使得 \(f_{l, k} \geq f_{k + 1, r}\) 的 \(k\)。
发现若钦定 \(r\),则该分界点 \(k\) 随 \(l\) 不降而不降。进而进一步分析确定分界点后的转移方程。
-
\(f_{l, r} = \min_{l \leq k < r}(f_{l, k} + a_k)\)
此时要想使得 \(f_{l, k}\) 取最小,必须发现它单调不减,且 \(a\) 数组也单调不减,进而最小值就是 \(k\) 取区间分界点的情况,两者都是最小的。 -
\(f_{l, r} = \min_{l \leq k < r}(f_{k + 1, r} + a_k)\)
可以认为 \(r\) 后面挂着一个长度为 \(r - k + 1\) 的窗口,要求维护最小的 \(f_{k + 1, r} + a_k\)。滑动窗口即可。
这道题我仍然没有理解透,需要回过头再看。是一道好题。