「笔记」可撤销背包
写在前面
vp 24 harbin 时 E 前面的一切全都会了就是不会撤销背包,以为要上多项式科技于是跑路了,vp 快结束了跟坐牢计算几何的 dztlb 大神一说他说他会呃呃,完蛋。
引入
给定 个物品,第 个物品体积为 。现在有一个容积为 的背包。
对于每一个物品 ,求删去该物品后使用剩余的物品,装满容积为 的背包的方案数,答案对 10 取模(即仅需输出十进制的末尾数字)。
。
1S,256MB。
分析
先考虑无删除,则有经典的状态 表示考虑到前 个物品,恰好装满容积为 的背包的方案数。初始化 ,则有显然的转移方程:
显然第一维可以通过滚动数组进行优化。
然后考虑如何通过上述状态得到撤销后的背包状态。发现上述背包计数的状态本质上是一个多项式,转移方程本质上就是每次对所有状态乘一个单项式。因此单次的可撤销背包本质上即每次对所有状态除一个单项式。对于物品 ,记 表示删除第 个物品后,恰好装满容积为 的背包的方案数,则有:
正确性显然,力大砖飞展开所有状态对应的多项式后,上式的结果与直接对删除物品后做背包的结果一致。从实际情况考虑,上式中 表示可能有物品 出现的方案的数量,而若某个包含物品 的方案在其中有贡献,则该方案中其他物品的容积之和一定为 ,即一定会对 有贡献。
总时间复杂度 级别。
代码
复制复制// /* 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; }
写在最后
参考:
想休学了。
作者@Luckyblock,转载请声明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通