DP优化学习笔记
2026-02-24
1 前缀和优化DP
1.1 核心思想
用空间换时间
通过预处理前缀和数组,将求和操作转化为前缀和数组的两次访问
1.2 常见可优化类型
1.2.1 一维DP:区间求和
状态定义:
\(dp[i]\) 表示考虑前 \(i\) 个元素或以 \(i\) 结尾的答案转移方程:
\[dp[i] = \sum_{j = L[i]}^{R[i]} g(j) + C[i]$ \]优化:
预处理 \(g(j)\) 的前缀和
\[sum[j] = \sum_{x=0}^{j} g(x) \]则 \(dp[i] = sum[R[i]] - sum[L[i]-1] + C[i]\)
1.2.2 二维DP:由上一层的连续区间转移
状态定义:
\(dp[i][j]\) 表示处理完前 \(i\) 个物品,当前状态为 \(j\) 的答案
转移方程形式:
\[dp[i][j] = \sum_{k = L}^{R} dp[i-1][k] \]优化:
在计算第 \(i\) 层之前,先预处理出第 \(i-1\) 层状态的前缀和
\[sum[i-1][k] = \sum_{x=0}^{k} dp[i-1][x] \]则 \(dp[i][j] = sum[i-1][R] - sum[i-1][L-1]\)
1.2 习题讲解
1.2.1 跳跃游戏 (Jump Game)
链接 : 暂无
1.2.1.1 暴力DP思路
状态定义:
\(dp[i]\) 表示是否能到达站点 \(i\)
转移方程:
\(dp[i] = true\),如果存在一个 \(j\) 满足 \(j\) 可到达,\(S[j]=='0'\),且 \(i\) 在 \(j\) 的跳跃范围内
1.2.1.2 前缀和优化分析
观察:
对于不同的 \(i\):
\(dp[i]\) 的值总是由上一段连续区间内的 \(dp\) 值之和决定
优化方法:
维护一个前缀和数组 \(preSum\)\[preSum[i] = \sum_{x=0}^{i} dp[x] \]那么,\(dp[i]\) 所需区间的和可以 \(O(1)\) 计算:
\(区间和 = preSum[i - minJump] - preSum[max(0, i - maxJump) - 1]\) 注意处理边界
这样就将
1D/1D的DP优化为了1D/0D
1.2.2 Candies
1.2.2.1 暴力DP思路
状态定义:
\(dp[i][j]\) 表示分配到第 \(i\) 个孩子,已经分掉了 \(j\) 颗糖的方案数。
转移方程:
第 \(i\) 个孩子拿 \(k\) 颗糖,则前 \(i-1\) 个孩子分掉了 \(j-k\) 颗糖\[dp[i][j] = \sum_{k = max(0, j-a_i)}^{j} dp[i-1][j-k] \]复杂度:
\(O(N * K^2)\),会超时
1.2.2.2 前缀和优化分析
观察:
\(dp[i][j]\) 的值由第 \(i-1\) 层状态的 一段连续区间 \([max(0, j-a_i), j]\) 的和决定
优化方法:
在计算第 \(i\) 行之前,预处理第 \(i-1\) 行的前缀和数组 \(sum_prev\):
\[sum_{prev}[x] = \sum_{t=0}^{x} dp[i-1][t] \]那么转移变为:
\(dp[i][j] = sum_{prev}[j] - sum_{prev}[max(0, j-a_i) - 1]\)
注意边界,当下标为-1时,值为0
复杂度降为 \(O(N * K)\)
1.2.3 逆序对数列
链接 : Luogu: P2513 [HAOI2009] 逆序对数列
刷表法见 2.3.2
1.2.3.1 填表法DP思路
状态定义:
\(dp[i][j]\) 表示用前 \(i\) 个数组成的排列,逆序对数为 \(j\) 的方案数。
转移方程:
插入到最后一个位置,新增 \(0\) 个逆序对
插入到倒数第二个,新增 \(1\) 个逆序对
...
插入到第一个,新增 \(i-1\) 个逆序对\[dp[i][j] = \sum_{k=0}^{min(j, i-1)} dp[i-1][j-k] \]
1.2.3.2 前缀和优化分析
观察:
与Candies问题惊人地相似!
\(dp[i][j]\) 仍然是由第 \(i-1\) 层的一段连续区间求和得到
区间的长度受 \(i-1\) 限制优化方法:
同样,预处理第 \(i-1\) 层的前缀和
\[sum[t] = \sum_{x=0}^{t} dp[i-1][x] \]则转移可优化为:
dp[i][j] = sum[j] - (j-i >= 0 ? sum[j-i] : 0)复杂度从 \(O(n^2 * k)\) 降为 \(O(n * k)\)
1.2.4 ABC253E Distance Sequence
1.2.4.1 暴力DP思路
状态定义:
\(dp[i][j]\) 表示第 \(i\) 个数字为 \(j\) 的方案数。
转移方程:
\[dp[i][j] = \sum_{l=1}^{j-K} dp[i-1][l] + \sum_{l=j+K}^{M} dp[i-1][l] \]
1.2.4.2 前缀和优化分析
观察:
这次需要求的是第 \(i-1\) 层状态的两段不连续区间的和。但这两段区间本身仍然是连续的
优化方法:
预处理第 \(i-1\) 层的前缀和
\[sum[x] = \sum_{t=1}^{x} dp[i-1][t] \]则转移可优化为:
左区间和 = sum[max(0, j-K)] 右区间和 = sum[M] - sum[min(M, j+K-1)] dp[i][j] = 左区间和 + 右区间和复杂度从 \(O(N * M^2)\) 降为 \(O(N * M)\)
2026-02-25
2 线段树优化DP
2.1 核心思想
利用线段树的区间查询能力快速获得转移所需的决策集合,从而将每次转移的复杂度从 \(O(n)\) 降至 \(O(log_n)\)
2.2 常见可优化类型
2.2.1 1D/1D DP,按权值条件转移
转移方程形如:
\[dp[i] = 1 + \sum_{j < i, a[j] < a[i]} dp[j] \]优化:
建立线段树
以 \(a[j]\) 为下标维护 \(dp[j]\) 的区间和
转移时查询小于 \(a[i]\) 的区间和,转移后更新 \(a[i]\) 位置的值。
2.2.2 2D/1D DP,按层转移加权值限制
转移方程形如:
\[dp[i][k] = \sum_{j < i, a[j] < a[i]} dp[j][k-1] \]优化:
对每一层 \(k\) 建立一棵线段树
维护该层所有 \(dp[j][k-1]\) 按 \(a[j]\) 的分布
转移时查询小于 \(a[i]\) 的区间和,转移后更新对应权值
2.3 习题讲解
2.3.1 严格上升子序列的数量
链接 : 暂无
2.3.1.1 暴力DP思路
状态定义:
\(dp[i]\) 表示以第 \(i\) 个元素结尾的严格上升子序列个数
转移方程:
\[dp[i] = 1 + \sum_{j < i, a[j] < a[i]} dp[j] \]直接转移复杂度 \(O(n^2)\),不可行
2.3.1.2 线段树优化分析
观察:
转移只关心所有满足 \(a[j] < a[i]\) 的 \(dp[j]\) 之和
这天然适合用权值线段树维护:将 \(a\) 离散化后,线段树的每个叶子节点对应一个权值,维护该权值上所有 \(dp[j]\) 的和
转移过程:
- 查询线段树中区间 \([1, a[i]-1]\) 的和,得到 \(sum\)
- 计算 \(dp[i] = sum + 1\)
- 将 \(dp[i]\) 累加到线段树中 \(a[i]\) 对应的位置上。
时间复杂度:
\(O(n log n)\)
2.3.2 逆序对数列
链接 : Luogu: P2513 [HAOI2009] 逆序对数列
填表法见 1.2.3
PS : 嗨嗨嗨,没想到我又来了吧 O(∩∩)O
2.3.2.1 刷表法DP思路
状态定义:
\(dp[i][j]\) 表示用前 \(i\) 个数组成的排列,逆序对数为 \(j\) 的方案数。
转移方程:
插入到最后一个位置,新增 \(0\) 个逆序对
插入到倒数第二个位置,新增 \(1\) 个逆序对
……
插入到第一个位置,新增 \(i-1\) 个逆序对\[dp[i][j] = \sum_{k=0}^{\min(j, i-1)} dp[i-1][j-k] \]
2.3.2.1 线段树优化刷表法
状态定义:
\(dp[i][j]\) 表示由 \(1 \sim i\) 组成的排列中,逆序对数量恰好为 \(j\) 的方案数
转移方程:
\[dp[i][j] = \sum_{t=0}^{\min(i-1,\, j)} dp[i-1][j-t] \]其中 \(t\) 表示将数字 \(i\) 插入到长度为 \(i-1\) 的排列时新产生的逆序对数
2026-02-27
3 单调队列优化DP学习笔记
3.1 核心思想
核心思想:
维护一个双端队列
其中的元素按照其对应的权重 \(dp[j] + g(j)\) 单调排列,同时保证下标在窗口内
这样队头就是当前窗口的最优决策
3.2 单调队列 / 滑动窗口
3.2.1 单调队列解法
维护一个队列:
- 存储可能成为最值的元素下标
- 保持队列中下标对应的值单调
- 每次窗口移动时:
- 掐头:若队头下标已滑出窗口,弹出
- 取值:队头即为当前窗口最值
- 去尾:从队尾弹出所有比当前元素更劣的元素
- 入队:将当前下标加入队尾
时间复杂度:
\(O(n)\)
3.3 习题讲解
3.3.1 切蛋糕
链接 : Luogu: P1714 切蛋糕
3.3.1.1 DP 思路
\(sum[i]\) 为前缀和
以 \(i\) 结尾的最大子段和为:
\[dp[i] = \max_{i - m \le j \le i - 1} \{ sum[i] - sum[j] \} \]分离变量:
\[dp[i] = sum[i] + \max_{j \in [i-m, i-1]} \{ -sum[j] \} \]即求滑动窗口内 \(-sum[j]\) 的最大值,等价于求 \(sum[j]\) 的最小值
3.3.1.2 单调队列优化
维护一个单调递增队列,窗口长度为 \(m\)
遍历 \(i\) 时:
- 掐头:若队头下标 \(< i - m\),弹出
- 取值:\(ans = \max(ans, sum[i] - sum[q[front]])\)
- 去尾:从队尾弹出所有 \(sum[q[tail]] \ge sum[i]\) 的元素
- 入队:将 \(i\) 加入队尾
3.3.2 琪露诺
3.3.2.1 DP 思路
状态定义:
设 \(dp[i]\) 为到达点 \(i\) 的最大价值,则:
转移方程:
\[dp[i] = A[i] + \max_{i - R \le j \le i - L} dp[j] \]转移区间是滑动窗口,符合单调队列条件
3.2.2 优化
用单调递减队列维护 \(dp[j]\) 的最大值
注意:
需要从 \(L\) 开始计算,且最终答案是 \(\max_{i > n - L} dp[i]\)

学习DP优化时记的笔记
浙公网安备 33010602011771号