「笔记」可撤销背包

写在前面

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

引入

P4141 消失之物

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

分析

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

fi,j=fi1,j+fi1,jwi

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

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

gj=fjgjwi

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

总时间复杂度 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 @   Luckyblock  阅读(137)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示