3.2 DP

\({\Large 约定}\)

  1. 用集合符号表示位运算符号,用 $ \oplus $ 表示异或,特别的,$ i \in S$ 表示二进制数 \(S\) 的第 \(i\) 位为 \(1\)
  2. \(V\) 表示值域,\(\sum\) 表示字符集,\(\omega\) 表示 bitset 的常数 \((\omega = 64)\)
  3. 除去用 \(()/[]\) 表示开闭区间外,\([]\) 仅表示艾弗森约定,\(\{\}\) 仅表示集合,括号嵌套全用 \(()\)
  4. 字符串或序列角标为区间表示对应区间的子串

problems

\(\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 ;
  }

submission

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 ;
  }

submission

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 ;
  }

submission

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 ;
  }

参考文献1
参考文献2

submission

2026.01.19

posted @ 2026-01-19 09:05  yqfff_qwq  阅读(5)  评论(0)    收藏  举报