2023 国庆集训 Day 4B 笔记
更佳的阅读体验:2023 国庆集训 Day 4B 笔记,本文同步发表于 洛谷博客。
以下内容为个人总结,如有错误或您有不同见解,欢迎指出。
单调队列优化 DP
洛谷 P1886 - 单调队列
int a[N];
deque<int> q; // 存每个数的下标
for (int i = 1; i <= n; ++i) {
while (!q.empty() && q.front() <= i - k) q.pop_front();
while (!q.empty() && a[q.back()] <= a[i]) q.pop_back();
q.push_back(i);
cout << a[q.front()] << '\n';
}
洛谷 P2216 - 理想的正方形
洛谷 P2219 - 修筑绿化带
- 求出每一块花园中,花坛应该放在哪里。如果花园的右下角为
(x, y) ,那么花坛的右下角就应位于(x - a + c + 1, y - b + d + 1) \sim (x - 1, y - 1) 这个矩形内。 - 求出这个矩形内最小的
sum 。
求最大全 0 正方形
给定一个 0 / 1 矩阵,求出最大的全为 0 的子正方形,求不带
带
- 令
f[i][j] 表示矩形(i, j) 位置往下延伸0 的个数,得到转移方程f[i][j] = \begin{cases} 0, a_{i, j} = 1 \\ f[i + 1][j] + 1, a_{i, j} = 0 \end{cases} 。 - 枚举正方形的上边界
u ,依次从1 \sim n 枚举正方形的右边界。 - 维护一个左边界的指针,并用单调队列维护左右边界之中的
f 值,需要时刻保证r - l + 1 \le \min f 。 - 复杂度为
O(n^2 ) 。
带修版本:洛谷 P4259 - 寻找车位
洛谷 P2254 - 瑰丽华尔兹
- 设
f[t][i][j] 为t 时刻到达(i, j) 的最长路程。 - 假如
t 时刻钢琴是往上方滑动,那么f[t][i][j] = \max (f[t - 1][i + 1][j] + 1, f[t - 1][i][j]) 。往其他方向同理。 - 复杂度为
O(nmT) 。 - 由于时间可以被分为
K 段,每段滑动方向相同,则可以修改状态,令f[k][i][j] 为第k 个时间段结束后到达(i, j) 的最长路程。 -
- 复杂度
O(nmK) 。
// 不考虑障碍 且只考虑向上的转移
for (int k = 1; k <= K; ++k) {
int t_k;
for (int j = 1; j <= m; ++j) {
deque<int> q;
int g[];
for (int i = 1; i <= n; ++i) g[i] = f[k - 1][i][j] + i;
for (int i = n; i; --i) {
while (!q.empty() && q.front() > i + t_k) q.pop_front();
while (!q.empty() && g[q.back()] <= g[i]) q.pop_back()
q.push_back(i);
f[k][i][j] = g[q.front()] - i;
}
}
}
洛谷 P4381 - Island
- 求若干个基环树的直径之和。
- 对于一个基环树,找到它的环,求出环上每个节点
i 环外延伸的最长距离d_i 。 - 将长为
m 的环破为长为2m 的链。如果从节点i 外面走到节点j 外面,则距离为j - i + d_i + d_j ,同时要求i < j < i + n 。使用单调队列优化求解。 - 复杂度为
O(n) 。
单调队列优化多重背包
一共有
洛谷 P5665 - 划分
- 贪心地使最后一段的和尽可能小。
- 不严谨证明:如果最后一段
[l, r] 的和没到达下界,那么可以不断地把a_l 分给前一段。由于s_i < s_{i+1} ,分给前一段的贡献是2a_l s_i ,分给后一段的贡献是2a_l s_{i+1} ,则分给前一段更优,同时分给前一段也有利于后面的分段。 - 所以设
f[r] 为\max {l-1: 最后一段分成 [l, r] 可行} ,应有sum_r - sum_{l-1} \ge sum_{l-1}-sum_{f[l-1]} ,即sum_r \ge 2sum_{l-1} - sum_{f[l-1]} ,双指针即可。 - 复杂度为
O(n) 。
计数 DP
洛谷 P5824 - 十二重计数法
洛谷 P3702 - 序列计数
- 用总方案数去掉不含质数的方案数。
- 分别用 DP 求解,设
f[i][j] 为i 个数字,和\mod p = j 的方案数,矩阵快速幂加速。 - 复杂度
O(p^3 \log n ) 。
洛谷 P3773 - 吉夫特
- 由 Lucas 定理可知
C_n^m 是奇数↔(n\&m)=m 。 - 设
f[T] 表示结尾值为S 的序列个数,转移时枚举子集T ,如果pos[S] < pos[T] 则令f[T] 加上f[S] 。 - 复杂度为
O(3^w) 。
int a[N], pos[N];
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
pos[a[i]] = i;
}
for (int s = VALUE_MAX; s; --s) {
++f[s];
for (int t = (s - 1) & s; t; --t)
if (pos[s] < pos[t]) f[t] += f[s];
ans += f[s] - 1;
}
洛谷 P5664 - Emiya 家今天的饭
- 对「每种食材最多在一半菜中」进行容斥。用总的方案数减去某种食材过多的方案数,不会有两种食材同时过多。
- 总方案数易求。
- 枚举过多的食材种类
t ,设f[i][j][k] 表示前i 种烹饪方法,一共做了j 个菜,其中k 个使用t 的方案数。转移时枚举第i + 1 种烹饪方法是否选用,选用时是否使用t ,最后要求k > \frac{j}{2} 。 - 只需记录
2k - j 的值即可,最后要求此值> 0 。 - 复杂度为
O(n^2 m) 。