atcoder abc 159:F - Knapsack for All Segments(思维 + 贡献 + dp)
程序员文章站
2022-06-22 23:47:10
...
有一个 的朴素做法,对每个区间 进行 dp,显然是通过不了的,且转移复杂度为 O(1),没有优化的空间,得考虑从其它角度切入这题。
对于某个解,最左边的数字的下标为 ,最右边的数字的下标为 ,这组解对答案的贡献为:,考虑枚举每个数字作为一组解的最右边的点,对于第 i 个数字,需要知道前 个数字,每种权值和为 的方案的最左边的数字的下标和,这个可以 dp 得到,设 表示前 个数字,权值和为 v 的各种方案中,最左边的数字的下标和,显然 ,当 时,
第一维可以滚动,则每个数字作为最右边的数时对答案的贡献为:
注意特判转移时, 的情况,这时最左最右均为 i
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3 + 10;
const int mod = 998244353;
typedef long long ll;
#define lowbit(i) (i & (-i))
ll sum[maxn];
int n,m,k,a[maxn],s,dp[maxn];
int main() {
scanf("%d%d",&n,&s);
for (int i = 1; i <= n; i++)
scanf("%d",&a[i]);
int ans = 0;
for (int i = 1; i <= n; i++) {
if (s > a[i]) ans = (ans + 1ll * dp[s - a[i]] * (n - i + 1)) % mod;
else if (s == a[i]) ans = (ans + 1ll * i * (n - i + 1)) % mod;
for (int j = s; j >= a[i]; j--)
dp[j] = (dp[j] + dp[j - a[i]]) % mod;
dp[a[i]] = (dp[a[i]] + i) % mod;
}
printf("%d\n",ans);
}