「笔记」可撤销背包
写在前面
vp 24 harbin 时 E 前面的一切全都会了就是不会撤销背包,以为要上多项式科技于是跑路了,vp 快结束了跟坐牢计算几何的 dztlb 大神一说他说他会呃呃,完蛋。
引入
给定 \(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\),则有显然的转移方程:
显然第一维可以通过滚动数组进行优化。
然后考虑如何通过上述状态得到撤销后的背包状态。发现上述背包计数的状态本质上是一个多项式,转移方程本质上就是每次对所有状态乘一个单项式。因此单次的可撤销背包本质上即每次对所有状态除一个单项式。对于物品 \(i\),记 \(g_{j}\) 表示删除第 \(i\) 个物品后,恰好装满容积为 \(j\) 的背包的方案数,则有:
正确性显然,力大砖飞展开所有状态对应的多项式后,上式的结果与直接对删除物品后做背包的结果一致。从实际情况考虑,上式中 \(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;
}
写在最后
参考:
想休学了。