「笔记」可撤销背包

写在前面

vp 24 harbin 时 E 前面的一切全都会了就是不会撤销背包,以为要上多项式科技于是跑路了,vp 快结束了跟坐牢计算几何的 dztlb 大神一说他说他会呃呃,完蛋。

引入

P4141 消失之物

给定 \(n\) 个物品,第 \(i\) 个物品体积为 \(w_i\)。现在有一个容积为 \(m\) 的背包。
对于每一个物品 \(1\sim i\),求删去该物品后使用剩余的物品,装满容积为 \(1\sim m\) 的背包的方案数,答案对 10 取模(即仅需输出十进制的末尾数字)。
\(1\le n, m\le 2000, 1\le w_i\le m\)
1S,256MB。

分析

先考虑无删除,则有经典的状态 \(f_{i, j}\) 表示考虑到前 \(i\) 个物品,恰好装满容积为 \(j\) 的背包的方案数。初始化 \(f_{0, 0} = 1\),则有显然的转移方程:

\[f_{i, j} = f_{i - 1, j} + f_{i - 1, j - w_i} \]

显然第一维可以通过滚动数组进行优化。

然后考虑如何通过上述状态得到撤销后的背包状态。发现上述背包计数的状态本质上是一个多项式,转移方程本质上就是每次对所有状态乘一个单项式。因此单次的可撤销背包本质上即每次对所有状态除一个单项式。对于物品 \(i\),记 \(g_{j}\) 表示删除第 \(i\) 个物品后,恰好装满容积为 \(j\) 的背包的方案数,则有:

\[g_{j} = f_{j} - g_{j - w_i} \]

正确性显然,力大砖飞展开所有状态对应的多项式后,上式的结果与直接对删除物品后做背包的结果一致。从实际情况考虑,上式中 \(f_j\) 表示可能有物品 \(i\) 出现的方案的数量,而若某个包含物品 \(i\) 的方案在其中有贡献,则该方案中其他物品的容积之和一定为 \(j - w_i\),即一定会对 \(g_{j} - w_i\) 有贡献。

总时间复杂度 \(O(nm)\) 级别。

代码

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2010;
//=============================================================
int n, m, w[kN];
LL f[kN], g[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m;
  for (int i = 1; i <= n; ++ i) std::cin >> w[i];
  f[0] = 1;
  for (int i = 1; i <= n; ++ i) {
    for (int j = m; j >= w[i]; -- j) {
      (f[j] += f[j - w[i]]) %= 10;
    }
  }

  for (int i = 1; i <= n; ++ i) {
    for (int j = 0; j <= m; ++ j) g[j] = f[j];
    for (int j = w[i]; j <= m; ++ j) {
      g[j] = (f[j] % 10 - g[j - w[i]] + 10) % 10;
    }
    for (int j = 1; j <= m; ++ j) std::cout << g[j];
    std::cout << "\n";
  }
  return 0;
}

例题

AtCoder ABC321 F

水水板题,直接做即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5010;
const LL p = 998244353;
//=============================================================
int q, k;
LL f[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> q >> k;
  f[0] = 1;
  while (q --) {
    char opt; int x; std::cin >> opt >> x;
    if (opt == '+') {
      for (int i = k; i >= x; -- i) f[i] = (f[i] + f[i - x]) % p; 
    } else {
      for (int i = x; i <= k; ++ i) f[i] = (f[i] - f[i - x] + p) % p;
    }
    std::cout << f[k] << "\n";
  }
  return 0;
}

CF1111D

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
const LL p = 1e9 + 7;
//=============================================================
std::string s;
int n, q, cnt[53];
LL k, fac[kN], invfac[kN], f[kN], g[kN], ans[53][53];
//=============================================================
LL qpow(LL x_, LL y_) {
  LL ret = 1;
  while (y_) {
    if (y_ & 1) ret = ret * x_ % p;
    x_ = x_ * x_ % p, y_ >>= 1ll;
  }
  return ret;
}
int trans(char ch_) {
  return (int) ('A' <= ch_ && ch_ <= 'Z' ? ch_ - 'A' : ch_ - 'a' + 26);
}
void solve(int x_, int y_) {
  for (int i = 0; i <= n / 2; ++ i) g[i] = f[i];
  for (int i = cnt[x_]; i <= n / 2; ++ i) g[i] = (g[i] - g[i - cnt[x_]] + p) % p;
  for (int i = cnt[y_]; i <= n / 2; ++ i) g[i] = (g[i] - g[i - cnt[y_]] + p) % p;

  LL v = g[n / 2];
  if (cnt[x_] + cnt[y_] <= n / 2) v += g[n / 2 - cnt[x_] - cnt[y_]];
  ans[x_][y_] = ans[y_][x_] = k * v % p;
}
void init() {
  fac[0] = 1;
  for (int i = 1; i <= n; ++ i) fac[i] = fac[i - 1] * i % p;
  invfac[n] = qpow(fac[n], p - 2);
  for (int i = n - 1; i; -- i) invfac[i] = invfac[i + 1] * (i + 1) % p;

  for (auto ch: s) ++ cnt[trans(ch)];
  k = fac[n / 2] * fac[n / 2] % p;
  for (int i = 0; i < 52; ++ i) if (cnt[i]) k = k * invfac[cnt[i]] % p;

  f[0] = 1;
  for (int i = 0; i < 52; ++ i) {
    if (!cnt[i]) continue;
    for (int j = n / 2; j >= cnt[i]; -- j) {
      f[j] += f[j - cnt[i]], f[j] %= p;
    }
  }

  // std::cout << k << "\n";
  // for (int i = 0; i < 52; ++ i) std::cout << cnt[i] << " ";
  // std::cout << "\n";
  // for (int i = 0; i <= n / 2; ++ i) std::cout << "" << i << ": " << f[i] << "\n"; 

  for (int i = 0; i < 52; ++ i) {
    for (int j = i; j < 52; ++ j) {
      if (!cnt[i] || !cnt[j]) continue;
      if (i == j) {
        ans[i][j] = k * f[n / 2] % p;
        continue;
      }
      solve(i, j);
    }
  }
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> s; n = s.length();
  init();

  std::cin >> q;
  while (q --) {
    int x, y; std::cin >> x >> y;
    std::cout << ans[trans(s[x - 1])][trans(s[y - 1])] << "\n";
  }
  return 0;
}
/*
abba
2
1 4
1 2
*/

CCPC2024 Harbin E

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 510;
const LL p = 1e9 + 7;
const double eps = 1e-9;
//=============================================================
int n, m, x[kN], v[kN];
int cur[kN], cnt0, cnt1;
LL inv[kN], ans, invv[kN], prob[kN], prob1[kN], invprob1[kN], f[kN];
struct T {
  int u, id; 
  double t;
  bool operator < (const T sec_) const {
    return t < sec_.t;
  }
} t[kN * kN];
//=============================================================
LL qpow(LL x_, LL y_) {
  LL ret = 1;
  while (y_) {
    if (y_ & 1) ret = ret * x_ % p;
    x_ = x_ * x_ % p, y_ >>= 1ll;
  }
  return ret;
}
void init() {
  for (int i = 1; i <= n; ++ i) inv[i] = qpow(i, p - 2);
  for (int i = 1; i <= m; ++ i) cur[i] = n, prob[i] = 1, prob1[i] = 0, invprob1[i] = 0;
  cnt0 = 0, cnt1 = m, f[m] = 1;
}
void dp(T t_) {
  auto [u, id, tt] = t_;
  for (int j = 0; j <= m; ++ j) {
    if (cur[id] == n) {
      f[j] = f[j + 1];
    } else {
      if (j >= 1) f[j] = (f[j] - f[j - 1] * prob[id] % p + p) % p;
      f[j] = f[j] * invprob1[id] % p;
    }
  }

  bool flag = (cur[id] == n);
  while (cur[id] && 1.0 * x[cur[id]] / v[id] <= tt + eps) -- cur[id];
  prob[id] = cur[id] * inv[n] % p;
  prob1[id] = (n - cur[id]) * inv[n] % p;
  invprob1[id] = n * inv[n - cur[id]] % p;
  if (flag && cur[id] != n) -- cnt1;
  if (cur[id] == 0) ++ cnt0;

  if (cnt1 <= m / 2) {
    ans += 1ll * u * invv[id] % p * inv[n] % p * f[m / 2] % p;
    ans %= p;
  }
  
  for (int j = m; j >= 0; -- j) {
    LL temp = 0;
    temp = f[j] * prob1[id] % p;
    if (j >= 1) temp = (temp + f[j - 1] * prob[id] % p) % p;

    f[j] = temp;
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n >> m;
  for (int i = 1; i <= n; ++ i) std::cin >> x[i], x[i] = -x[i];
  std::sort(x + 1, x + n + 1, std::greater<int>());
  for (int i = 1; i <= m; ++ i) std::cin >> v[i], invv[i] = qpow(v[i], p - 2);
  
  for (int i = 1; i <= m; ++ i) {
    for (int j = 1; j <= n; ++ j) {
      t[(i - 1) * n + j] = (T) {x[j], i, 1.0 * x[j] / v[i]};
    }
  }
  std::sort(t + 1, t + n * m + 1);

  init();
  for (int i = 1; i <= n * m; ++ i) {
    dp(t[i]);
    if (cnt0 > m / 2) break;
  }
  std::cout << ans << "\n";
  return 0;
}

写在最后

参考:

想休学了。

posted @ 2024-11-11 15:40  Luckyblock  阅读(25)  评论(0编辑  收藏  举报