[题解]AT_abc321_f [ABC321F] #(subset sum = K) with Add and Erase
思路
可撤销背包板子。
首先问题是用当前所拥有的数的集合凑出 的方案数。
这个问题明显可以背包解决,即 。
但是,此问题中物品有可能会被删除,即变为了一个动态的问题,如果直接暴力计算时间复杂度为 。
那么,我们转化一下思路,如果我们使每一次变化的数 都假令为 中的最后一个元素。因为 0-1 背包对于数的先后顺序无关,所以此假令不会影响答案。
然后,再想一下 0-1 背包的板子,我们已经假令 为 中最后一位元素,如果 的大小为 ,那么我们在枚举 时,当 在 范围内时,此时更新的 DP 数组与原 DP 数组的值不变。
因此,我们只需要考虑 带来的贡献,我们将两种操作分开考虑。
Part 1 添加操作#
因为是在 0-1 背包里面取出最后一个元素,所以与 0-1 背包相同。
for (re int i = n;i >= x;i--) dp[i] = Add(dp[i],dp[i - x]);
Part 2 删除操作#
因为是删除,所以枚举的顺序也不同。
设想如果按照正常 0-1 背包的顺序枚举,当此时删除 时,如果能删除 就会被 所影响,但如果 中只有一个 ,显然 无法取到。
那么当倒序循环时, 会在 之后计算到。
for (re int i = x;i <= n;i++) dp[i] = Sub(dp[i],dp[i - x]);
Code
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 5010,mod = 998244353;
int q,n;
int dp[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline int Add(int a,int b){
return (a + b) % mod;
}
inline int Sub(int a,int b){
return ((a - b) % mod + mod) % mod;
}
signed main(){
dp[0] = 1;
q = read();
n = read();
while (q--){
int x;
char op[10];
scanf("%s",op);
x = read();
if (op[0] == '+'){
for (re int i = n;i >= x;i--) dp[i] = Add(dp[i],dp[i - x]);
}
else{
for (re int i = x;i <= n;i++) dp[i] = Sub(dp[i],dp[i - x]);
}
printf("%lld\n",dp[n]);
}
return 0;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18262943
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】