3.2 DP
\({\Large 约定}\):
- 用集合符号表示位运算符号,用 $ \oplus $ 表示异或,特别的,$ i \in S$ 表示二进制数 \(S\) 的第 \(i\) 位为 \(1\)
- 用 \(V\) 表示值域,\(\sum\) 表示字符集,\(\omega\) 表示
bitset的常数 \((\omega = 64)\)- 除去用 \(()/[]\) 表示开闭区间外,\([]\) 仅表示艾弗森约定,\(\{\}\) 仅表示集合,括号嵌套全用 \(()\)
- 字符串或序列角标为区间表示对应区间的子串
\(\textcolor{white}{\mathrm{pw:helloWORlD}}\)
P2627 [USACO11OPEN] Mowing the Lawn G
\(\textcolor{green}{\mathrm{普及+/提高}}\) tag 动态规划DP 单调队列
题目要求不能有连续的超过 \(K\) 只奶牛工作,等价于休息的奶牛之间距离不能超过 \(K\)。要求最大效率,等价于求最小的休息的奶牛的效率和。
发现这个非常可以单调队列去做,然后就做完了。
Code
const int N = 1e5 + 10;
int n, k;
int a[N];
ll sum;
ll f[N];
int q[N], he = 1, tl;
void main() {
read(n, k);
REP(i, 1, n) read(a[i]), sum += a[i];
q[++tl] = 0;
REP(i, 1, n + 1) {
while(he <= tl and i - q[he] > k + 1) he++;
f[i] = f[q[he]] + a[i];
while(he <= tl and f[i] <= f[q[tl]]) tl--;
q[++tl] = i;
}
std::cout << sum - f[n + 1];
return ;
}
2026.01.19
P3572 [POI 2014] PTA-Little Bird
\(\textcolor{green}{\mathrm{普及+/提高}}\) tag 动态规划DP 单调队列
看到 \(n\) 和 \(q\) 的范围猜测复杂度是 \(nq\) 的。对于每一个 \(k_i\),我们要在 \(\mathcal{O(n)}\) 的复杂度内求出答案。因为对于一只鸟 \(k_i\) 是不变的,考虑用单调队列去做。
假如说算完了到达当前位置的劳累值,显然,在这个位置往前 \(k_i\) 的范围内,如果存在一个结点的劳累值大于当前位置,或者在劳累值相等的情况下高度小于等于当前位置,是需要被弹出的,这样显然是不劣的。发现单调队列中其实是按照 \(f_i\) 为第一关键字、\(d_i\) 为第二关键字排序的。
Code
const int N = 1e6 + 10;
int n, m;
int a[N], k[N];
ll f[N];
int q[N], he = 1, tl;
void main() {
read(n);
REP(i, 1, n) read(a[i]);
read(m);
REP(i, 1, m) read(k[i]);
REP(j, 1, m) {
he = 1, tl = 0;
q[++tl] = 1;
REP(i, 2, n) {
while(he <= tl and i - q[he] > k[j]) he++;
f[i] = f[q[he]] + (a[i] >= a[q[he]]);
while(he <= tl and (f[i] < f[q[tl]] or (f[i] == f[q[tl]] and a[i] >= a[q[tl]]))) tl--;
q[++tl] = i;
}
std::cout << f[n] << '\n';
}
return ;
}
2026.01.19
P10143 [WC2024] 代码堵塞
\(\textcolor{green}{\mathrm{普及+/提高}}\) tag 动态规划DP 单调队列
如果直接求贡献会比较困难,所以考虑每一个位置上的数会在 \(2^n\) 种方案中出现几次。
我们发现,当 \(i\) 结果可见时,他能否提交只跟编号比他小的题目有关。
当 \(i\) 结果不可见时,他能否提交只跟编号比他大的题目有关。
因此,当 \(i\) 结果可见时,我们可以 dp 求出从 \(1\sim i-1\) 中选出若干个作为结果可见的且时间总和 \(\leq T-t_i\) 的方案数,其余的 \(n-i\) 个数状态不影响结果,要乘上 \(2^{n-i}\)。
当 \(i\) 结果不可见时,我们可以 dp 求出从 \(i+1\sim n\) 中选出若干个作为结果可见的且时间总和 \(\leq T-\displaystyle\sum_{j=1}^{i}t_j\) 的方案数,其余的 \(i-1\) 个数状态不影响结果,要乘上 \(2^{i-1}\)。
Code
const int mod = 998244353;
const int M = 210;
const int N = 3e5 + 10;
int C, n, T;
int a[M], t[M];
ll s[M];
ll f[N];
ll pw[N];
void main() {
read(C, n, T);
REP(i, 1, n) read(a[i]);
REP(i, 1, n) read(t[i]), s[i] = s[i - 1] + t[i];
pw[0] = 1;
REP(i, 1, n) pw[i] = pw[i - 1] * 2 % mod;
ll ans = 0;
f[0] = 1;
REP(i, 1, n) {
int cnt = 0;
DEP(j, T - t[i], 0) {
cnt = (cnt + f[j]) % mod;
f[j + t[i]] = (f[j + t[i]] + f[j]) % mod;
}
ans = (ans + (1ll * cnt * a[i] % mod * pw[n - i]) % mod) % mod;
}
memset(f, 0, sizeof(f));
f[0] = 1;
DEP(i, n, 1) {
int cnt = 0;
DEP(j, T - s[i], 0) cnt = (cnt + f[j]) % mod;
ans = (ans + (1ll * cnt * a[i] % mod * pw[i - 1]) % mod) % mod;
DEP(j, T - t[i], 0) f[j + t[i]] = (f[j + t[i]] + f[j]) % mod;
}
std::cout << ans << '\n';
return ;
}
2026.01.19
P3195 [HNOI2008] 玩具装箱
\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 动态规划DP 单调队列 斜率优化
斜率优化的板子题。
更多详情内容可以看下面的文章。
以这道题为例,令 \(sum_i=\sum_{j=1}^{i}a_j,a_i=sum_i+i,b_i=sum_i+i+L+1\)
Code
#define X(_) (b[_])
#define Y(_) (f[_] + 1ll * b[_] * b[_])
#define B(_) (f[_] - 1ll * a[_] * a[_])
#define K(_) (2ll * a[_])
const int N = 5e4 + 10;
int n, L;
ll c[N], a[N], b[N];
ll f[N];
int q[N], he = 1, tl;
void main() {
read(n, L);
REP(i, 1, n) read(c[i]), c[i] += c[i - 1], a[i] = c[i] + i, b[i] = c[i] + i + L + 1;
b[0] = L + 1;
f[0] = 0;
q[++tl] = 0;
REP(i, 1, n) {
while(he < tl and (Y(q[he + 1]) - Y(q[he])) <= 1ll * K(i) * (X(q[he + 1]) - X(q[he]))) he++;
f[i] = f[q[he]] + (a[i] - b[q[he]]) * (a[i] - b[q[he]]);
while(he < tl and 1ll * (Y(q[tl]) - Y(q[tl - 1])) * (X(i) - X(q[tl])) >= 1ll * (Y(i) - Y(q[tl])) * (X(q[tl]) - X(q[tl - 1]))) tl--;
q[++tl] = i;
}
std::cout << f[n] << '\n';
return ;
}
2026.01.19

浙公网安备 33010602011771号