序列上的DP
参考资料
连续段 DP
trick
-
很多 01 串问题,都是固定 0 或者 1,移动另外一种,这样会方便考虑很多。
-
如果题目需要考虑当前位选还是不选时,你可以多记一位,也可以强制选,在向后转移的时候在考虑不选时的状态。
-
最优序列划分常见的思路就是 DP。
-
有关颜色段最优合并的 DP 往往只有考虑每种颜色最后出现的位置即可。
-
平方权值。
-
dp 的设计和优化本质是合并同类结构的过程。
-
总是能想到对值来进行 DP,但是为什么会这么小气,不舍得多开几维状态,或者用上辅助数组,这点很重要!!!
题目
P17337 【MX-X30-T3】宇宙分解 \(\checkmark\)
题目大意
给定 01 串,可任意次执行:若相邻为 01,可删除右边的 1,或将 01 交换为 10。求最终能得到多少种不同的 01 串,对 998244353 取模。
\(T \le 10^3\),所有测试 \(n\) 之和 \(\le 10^5\),所有串中 \(1\) 的个数总和 \(S \le 2\times 10^3\)。
解题思路
解题思路
启示这道题的数据范围提示的很明显,复杂度是要 \(O(nS)\) 的。
固定 0,操作就相当于选择左移 1 或者删掉这个 1,由于当前第 \(i\) 位的取值依赖于 \(i-1\) 位的情况,所以考虑 DP。值得注意的是:1 之间的相对顺序不变,所以我们不需要考虑 1 的位置关系。
定义 \(f_{i,j}\) 表示强制选第 \(i\) 个 1,它的左边有 \(j\) 个 0,那么转移就是:
- 选 \(i-1\),\(dp_{i,j}=\sum_{k=0}^j dp_{i-1,k}\)
- 不选 \(i-1\),此时 \(i-1\) 在哪已经不重要了,我们不考虑 1 的位置关系,所以无论 \(i-1\) 在哪贡献的序列都是相同的,所以跟上面是等价的,但是还要 \(dp_{i,j}+1\)。这里的 \(+1\) 是因为可能存在 \(1\sim i-1\) 的 1 全部删掉了的情况。
这个很容易用前缀和优化,在加个滚动数组即可。
一个小提示,如果该序列前缀都是 1 的话,他们对答案没有任何贡献,不考虑。
启示: 很多这种 01 串问题,都是固定 0 或者 1,枚举移动另外一种,这样会方便考虑很多。还有一点,如果题目需要考虑当前位选还是不选时,你可以多记一位,也可以强制选,在向后转移的时候在考虑不选时的状态。
P11311 漫长的小纸带 \(\checkmark\)
题目大意
将序列 \(a_1,\dots,a_n\) 划分为若干连续段,每段代价为该段内不同元素种类数的平方,求最小总代价。
\(1\le n\le 2\times10^5,\ 1\le a_i\le 10^9\)。
解题思路
先考虑朴素做法,定义 \(f_i\) 表示考虑到当前位置 \(i\) 的最优划分,\(f_i=\min_{1\le j< i} f_{j}+w(j+1,i)\),\(w(j+1,i)\) 可以在跟新 \(i\) 的时候动态维护,时间复杂度 \(O(n^2)\)。
考虑优化,真正有用的 \(j\) 其实是每种不同权值的最后出现位置,所以我们对于每个权值只用考虑一次即可,可以用 set 维护。并且注意到 \(f_i\) 上限是 \(i\),所以只用往回跳 \(\sqrt n\) 次即可。总复杂度是 \(O(n\sqrt n+n\log n)\)。
启发:
-
有关颜色段最优合并的 DP 往往只有考虑每种颜色最后出现的位置即可。
-
注意到 \(f_i\) 的上限并且平方非常神秘。
-
其实也容易注意到 \(f\) 是单调的而且 \(w\) 是有凸性的,所以可以用
AT_abc468_f [ABC468F] Chmax \(\checkmark\)
题目大意
给定一个长度为 \(n\) 的序列 \(a\),你现在有个三元组 \((x,y,pts)\),从 \(1\sim n\) 考虑,你现在有两种操作(选一种):
- 如果 \(x<a_i\),\((x,y,pts)\rightarrow (a_i,y,pts+1)\)
- 如果 \(y<a_i\),\((x,y,pts)\rightarrow (x,a_i,pts+1)\)
求 \(pts\) 可能的最大值。\(n\le 10^6\)
解题思路
容易发现,每个前缀最大值是肯定要选的,并且发现这些最大值全都选到 \(x\) 即可,再把剩下的跑一个 \(O(n\log n)\) 的最长上升子序列即可。
P15389 k-绍兴序列 / splay \(\checkmark\)
题目大意
长度为 \(n\)、每个元素在 \([0,m]\) 内的非负整数序列称为 \(k\)-绍兴序列,当且仅当存在 \(1\le i<j\le n\) 且 \(a_j\ne0\),使得 \(\frac{a_i}{a_j}<k\);若不存在这样的 \(i,j\)(如全零或 \(n=1\)),则不是 \(k\)-绍兴序列。求 \(k\)-绍兴序列的数量 \(\bmod 10^9+7\)。
\(T\le256,\ n,m,k\le 2\times10^5\)。
解题思路
首先肯定要转化成统计不是 \(k-绍兴序列\) 的数量。特判 \(n=1\) 和 \(k=1\),\(k=1\) 就是上升序列计数,答案是 \(n+m\choose m\)。对于正常的情况,由于我们要保证 \(\forall 1\le i<j\le n,a_i\ge a_jk\),因此,这个非零的数一定是一个前缀,并且最多由 \(\log_km\) 个 ,所以直接对值域进行 DP,进行 \(\log_k m\) 轮,每轮值域 \(\times{1\over k}\),加上前缀和优化。时间复杂度 \(O(m)\)。
启发: 需要通过观察一些性质来简化问题。
[ABC134F] Permutation Oddness \(\checkmark\)
题目大意
求 \(1\sim n\) 的排列 \(p\) 中,满足 \(\sum_{i=1}^n |i-p_i|=k\) 的排列个数,对 \(10^9+7\) 取模。
\(1\le n\le 50,\ 0\le k\le n^2\)。
解题思路
对于这种带绝对值的题目,有一种比较好的切入角度:拆贡献。
事实上我们要考虑两个的贡献,\(i\) 和 \(p_i\) 的贡献。所以我们要将 \(i\) 与 \(p\) 一起考虑。
\(i,p_i\) 他们之间的地位是等价的,这启发我们对值 dp,同时填 \(i,p_i\)。
稍微转化一下:原问题相当于给你两个排列 \(a,b\),你需要将他们的元素两两配对。你需要求出 \(|a_i-b_i|\),我们把值相等的 \(a_i,b_j\) 放在一起考虑。我们从小到大枚举权值 \(i\),分讨转移 \(a,b\) 的匹配情况。
思考一下我们需要把什么记录进状态,首先权值 \(i\) 是一定要有的(他相当于一个阶段划分);然后是 \(k\),也就是当前的 \(\sum |a_i-b_i|\);还有什么,我们还要知道匹配情况,我们要知道 \(<i\) 的有多少没有配对,\(>i\) 的有多少没有配对,事实上,我们只要知道 \(<i\) 的有多少便可以自然推出 \(>i\) 的数量。
所以定义出 \(f(i,j,k)\) 来计数,状态转移方程如下:
-
\(a,b\) 两个自己配对 \(f(i,j,k)\gets f(i-1,j,k)\)
-
\(a,b\) 两个都和前面的配对 \(f(i,j,k) \gets f(i-1,j+1,k-2i)\times(j+1)^2\)。
-
\(a,b\) 两个都准备和后面的配对 \(f(i,j,k)\gets f(i-1,j-1,k+2i)\)。
-
\(a,b\) 一个在前一个在后,由于 \(i\) 单调枚举,所以 \(<i+1\) 的未配对数不变 \(f(i,j,k)\gets 2\times f(i-1,j,k)\times j\)。
注意,由于 \(k\) 这个可能会从负数转移过来,所以集体偏移 \(n^2\) 即可。
启发:
- 绝对值拆贡献是老生常谈了。
- 这道题的转化与设计是非常妙的。
CF1768F Wonderful Jump \(\checkmark\)
题目大意
有 \(n\) 个位置,第 \(i\) 个位置有权值 \(a_i\)。从位置 \(i\) 向右跳到位置 \(j\)(\(i\le j\))的花费为 \(\min(a_i,\dots,a_j)\times(j-i)^2\)。对每个 \(k=1,\dots,n\),求从位置 \(1\) 跳到位置 \(k\) 的最小总花费。
\(n\le 4\times 10^5,\ 1\le a_i\le n\)。
解题思路
首先由非常显然的 \(O(n^2)\) 最短路,但这样显然是过不了的。
这道题有非常妙的观察,可以辅助优化。
性质 1
最简单的,\(f_i\) 显然不可能从后面转移过来,但他不一定单调。
性质 2
首先,最小值一定要放在跳的区间两侧才是最优的。比如说,\(\min=a_k\),\(a_i\to a_j\) 不如 \(a_i\to a_k\to a_j\),这个用基本不等式便可以证出。
性质 3
每次从最小值 \(a_i\) 跳的步长不超过 \(n\over a_i\),跳到 \(a_i\) 也同理。我们从 \(a_i\) 跳到其他格子有两种方法,一步一步跳和一步跳。
一步一步跳最劣也就是 \(nd\),而一步跳到是 \(a_id^2\),我们枚举步长 \(d\) 一步跳到比一步一步跳要求 \(a_id^2\le nd\),也就是 \(d\le {n\over a_i}\)。
我们根据以上重要性质可以优化转移过程,我们对每个 \(i\),以 \(a_i\) 作为最小值去转移,每次转移到的点不超过 \(2n\over a_i\)。
分析一下时间复杂度 \(O(n\sqrt n)\),考虑根号分治:
- \(a_i\ge \sqrt n\),步长不超过 \(\sqrt n\),所以总的是 \(O(n\sqrt n)\)。
- \(a_i<\sqrt n\),考虑将相同的 \(a_i\) 合在一起考虑,以 \(a_i\) 作为最小值的并起来长度为 \(O(n)\),而不同的 \(a_i\) 个数为 \(\sqrt n\),总的也是 \(O(n\sqrt n)\)。
实现的时候,枚举 \(2\le i\le n\),枚举步长,看看 \(f_i\) 能从哪里转移而来,然后能转移到哪里,就做完了。
启发:
-
先列出朴素的 DP 转移式子,观察重要性质。
-
通过一些特殊的情况限制答案范围,比如这道题用一步一步跳这种跳法筛选掉很多不合法的步长,缩小枚举的范围。
-
根号分治分析复杂度太牛了。
P17129 [ICPC 2025 Shanghai R] Round screws \(\checkmark\)
题目大意
有长度为 \(n\) 的非负整数序列 \(a\),定义价值 \(V=a_1+a_n+\sum_{i=1}^{n-1}(a_i\oplus a_{i+1})\)。可任意次修改任意元素为任意非负整数,每次花费 \(C\),求最小化修改总花费加修改后序列价值。多组数据。
\(T\le 100,\ n\le 10^5,\ C<2^{19},\ 0\le a_i<2^{18}\),所有测试用例 \(n\) 之和 \(\le 2\times 10^5\)。
解题思路
每次找到增量最大的贪心是错误的,因为这样子会有有后效性。
那么我们可以从最终结构来考虑,考虑对于一段 \([l,r]\) 里面的 \(a_i\) 全都要修改,那么全部改成 \(a_{l-1}\) 是最优的。因为无论如何,\(a_{l-1}\oplus a_{r+1}\) 的每一个二进制位一定要在 \(\sum_{i=l}^r a_i\oplus a_{i+1}\) 贡献至少一次,那么全部改成 \(a_{l-1}\) 或者 \(a_{r+1}\) 一定是最优的了。
对于这种问题,我们可以考虑保存哪些位置,这个其实就是经典的序列分段问题,套路化的,定义 \(f_i\) 表示考虑到当前位置 \(i\) 的最优方案。实现是可以现在头尾补 0(避免边界讨论),转移方程简单写出来:
答案就是 \(f_{n+1}+(n+1)C\)。
看到这种方程,很让人想到四边形不等式优化、斜率优化之类的,但是这个有一个 \(\oplus\) 就很烦人。
一般带 \(\oplus\) 要么用线性基,要么用 trie,但是这道题这两个都没法套上去。
接下来介绍一个 trick,听别人说这个叫做二进制平衡规划:
比如这道题中,\(a_i\le V=2^{18}\),\(V\) 并不大。我们拆成高低位,分块。具体来说,共有 \(2^9\) 个块,高 9 位作为块的编号,每个块长度为 \(2^9\),维护低 9 位的答案。
维护 \(g_{h,l}\) 表示高 9 位为 \(h\),低 9 位为 \(l\) 的最优答案,\(H_i\) 表示 \(a_i\) 的高 9 位,\(L_i\) 同理。这样 \(f_i=\min_h(g_{h,L_i}+2^9\times (H_i\oplus h)-C)\),这样复杂度是 \(O(n\sqrt V)\)。
考虑怎么计算 \(g_{h,l}\),其实就是每次计算完 \(f_i\),然后 \(g_{h,l}\gets f_i+(L_i\oplus l)\),这个复杂度也是 \(O(n\sqrt V)\) 的。
总时间复杂度是 \(O(n\sqrt V)\)。
启发:
- 非常妙的一道 trick 题,知道这个高低位分块的 trick 就很好做。
P13580 [CCPC 2024 重庆站] Median Replacement \(\checkmark\)
题目大意
给定每个位置 \(i\) 的取值区间 \([l_i,r_i]\),对所有可能的初始序列 \(a\)(\(l_i\le a_i\le r_i\)),可以反复选择长度大于 \(1\) 的奇数区间,将该区间内所有数替换为它们的中位数。最终序列所有数相等,设最终值为 \(x\),求所有可能初始序列对应的 \(x\) 的最大值之和,模 \(10^9+7\)。
\(T\le 10,\ 3\le n\le 150,\ 1\le l_i\le r_i\le 10^9\)。
解题思路
先考虑判定,也就是给定序列 \(a\),它的最终值能否 \(\ge x\)。经典地,将 \(\ge x\) 的替换成 1,\(<x\) 的替换成 0。容易发现,只要存在相邻两个 1 间隔 \(\le 1\),那么就一定可以替换成 \(\ge x\) 的数。
接下来计算值 \(\ge x\) 的序列个数,记之为 \(c_x\)。运用补集转换的思想,我们可以计算出不存在两个 1 的下标差 \(\le 2\)(即任意两个 1 的下标差 \(> 2\))的序列个数 \(c'_x\),则 \(c_x = \prod_{i=1}^n (r_i - l_i + 1) - c'_x\)。
考虑使用动态规划求解 \(c'_x\)。 令 \(f_{i,0/1/2}\) 表示仅考虑序列中前 \(i\) 个数,且第 \(i\) 位和第 \(i-1\) 位分别 \((< x, < x) / (< x, \ge x) / (\ge x, < x)\) 的 \(c'_x\) 的值,则转移方程为:
则 \(c'_x = f_{i,0} + f_{i,1} + f_{i,2}\),\(c_x = \prod_{i=1}^n (r_i - l_i + 1) - (f_{i,0} + f_{i,1} + f_{i,2})\)。
(当然这里也可不用补集转换直接动态规划求解 \(c_x\),但状态会多出一维,常数也会增加。)
现在我们所要求的答案就是 \(\sum_x (c_x-c_{x+1})\times x=\sum_x c_x-c_{x+1}\times (x+1)=\sum_x c_x\)。
令 \(g(n)=\sum_{1\le i\le n} c_i\),我们最终的答案就是 \(\sum_i g(i)\)。这样就得到了 \(O(TnV)\)。
接下来考虑优化,\(n\) 很小,但是值域很大,我们能做什么优化呢?拉格朗日插值?矩阵乘法?显然矩阵乘法不可行。拉格朗日插值很难处理 \(\min \max\) 的情况下。怎么办呢?
我们把 \(l_i,r_i\) 全部拿出来一起放在 \(p\) 里,排一下序,答案其实就是 \(\sum f(p_i+1,p_{i+1})\),其中 \(f(l,r)=\sum_{i=l}^r g(i)\)。为什么这样做,因为 \([p_i+1,p_{i+1}]\) 在做 DP 的时候有很多类似的地方,它们在上方 DP 中 \(\min\max\) 这些操作的取值相同,而每次到了分界点 DP 值都会有所变化。这样一旦 \(\min\max\) 固定下来,我们就可以在一段区间中用拉格朗日插值了,因为 \(g\) 最多是 \(n+1\) 次多项式, \(f\) 也就是 \(g\) 的前缀和也是 \(n+1\) 次,然后用拉格朗日差值跑完 \(n+2\) 个点,然后就可以算出其他的了。
复杂度 \(O(Tn^3)\)。
[AGC024E] Sequence Growing Hard \(\checkmark\)
题目大意
有数列组 \((A_0,A_1,\dots,A_N)\),其中 \(A_i\) 是长度 \(i\) 的 \(1\sim K\) 整数序列,\(A_{i-1}\) 由 \(A_i\) 删除一个元素得到,且 \(A_i\) 字典序严格大于 \(A_{i-1}\),求满足条件的数列组数量 \(\bmod M\)。
\(1\le N,K\le 300,\ 2\le M\le 10^9\)。
解题思路
我们发现你要在位置 \(i\) 插入一个数 \(x\),这就要求 \(x>a_i\),因为 \(x=a_i\) 的情况与在相等的 \(a_i\) 连续段后面插上 \(x=a_i\),会算重,所以要求 \(x>a_i\)。
这道题对于下标来进行 DP 几乎是不可做的。于是换一种考虑方式,对值来进行 DP 来插入,我们还关注目前有多少位,所以考虑记 \(f_{i,j}\) 表示当前最大的插入值为 \(i\),\(1\sim i\) 一共插了 \(j\) 位的方案数。转移就是:
\(f_{i,j}\gets f_{i-1,k}\times g_{k,j-k}\)
辅助数组 \(g_{i,j}\) 表示分别在 \(len\in{[0,i]}\) 的时候插入 \(a_{len}\) 个数,最后 \(\sum a_{len}=j\) 的方案数。转移就是:
\(g_{i,j}\gets g_{i-1,k}\times (i+1)^{j-k}\)
初始化 \(g_{0,i}=1,f_{0,0}=1\),答案就是 \(f_{k,n}\)。
总复杂度 \(O(n^2k)\)。
启发:
-
总是能想到对值来进行 DP,但是为什么会这么小气,不舍得多开几维状态,或者用上辅助数组,这点很重要!!!
-
这道题其实也可以从树的角度考虑,记得问问gzj,这个也可以学一下,学习将其转化成树上问题的一种思想。
P11218 【MX-S4-T2】「yyOI R2」youyou 不喜欢夏天 \(\checkmark\)
题目大意
给定一个 \(2\times n\) 的黑白网格。youyou 先选一个可以为空的四连通格子集合 \(S\);之后 yy 可以选择至多 \(m\) 列,将每列上下两个格子的颜色互换。youyou 希望最大化最终 \(S\) 中黑格数减白格数,yy 希望最小化它。双方均最优,求最终的黑格数减白格数。
多组数据:\(1\le T\le 5\),\(1\le m\le n\le 2\times 10^6\),\(\sum n\le 2\times 10^6\)。
解题思路
比较经典套路化的一道题了。
我们发现 \(m\) 次交换这个很烦人,我们先不考虑,每一列的情况可能是 00,10,01,11 四种可能的情况,选法也是类似。其中 00,11 对答案的贡献是好算的,01,10 在不考虑 \(m\) 的情况下也可以解决。这是一个简单的 \(dp\) 问题,设 \(f_{i,j}\) 表示考虑到第 \(i\) 列的选的情况,\(j\in[0,4]\) 对应着不同的选法。
我们现在考虑加回去 \(m\) 次交换的限制,我们设需要进行交换次数为 \(opt\),可以分类讨论一下:
-
\(opt<m\) 即所有对应的 01,10 中的选单个都要被交换,那么我们可以强制钦定选单个的贡献是 -1,即使这一列的贡献是 \(00,11\),但是因为这样肯定不优,所以对答案其实没有影响。
-
\(opt>m\) 即可能有的 01,10 不会被交换,那么我们钦定选单个的贡献是 +1/-1(由当前是 1 还是 0 决定),最后答案再减去 \(2m\) 即可。
以上两种情况取 \(\max\) 即可。
启发:
- 这道题最恶心的是操作次数的限制,无法计入状态里面,那么我们可以分类讨论一下,分成不超过和超过,钦定一些贡献系数,最后答案取个 \(\max\) 即可。里面融合的重要思想是即使方案不合法但也会因为更劣而不被计入答案。
P2300 合并神犇 \(\checkmark\)
题目大意
给定长度为 \(n\) 的序列 \(p_i\)。每次可将一个元素合并到相邻元素,新元素值为两者之和,元素个数减 \(1\),且合并后不可拆分。求最少合并多少次,才能使最终序列从左到右单调不减。
\(0<n\le 2\times 10^5\),\(0<p_i\le 2147483647\)。
解题思路
首先肯定可以转化成比较好做的形式,划分成尽可能多的区间,使得这些区间和按照非递减顺序排列。
经典的 DP,考虑使用 \(f_{i,lst}\) 表示考虑到第 \(i\) 个数,最后一段权值 \(lst\) 划分的最大块数,发现块数越多,\(lst\) 越小,所以不用把 \(lst\) 计入状态。
直接做是 \(O(n^2)\) 的,可以使用堆优化成 \(O(n\log n)\),当然容易发现,决策点是有单调性的,所以我们直接用用单调栈维护即可。
启发:
- 尝试转化成自己熟悉的问题,分析相关性质。

浙公网安备 33010602011771号