[AtCoder ABC159F] Knapsack for All Segments

题目链接

Solution

这种题首先肯定是转换视角:假设 $A_i\cdots A_j\cdots A_k$ 是和为 $S$ 的序列,则它对答案的贡献为 $i\cdot(n-k+1)$.

于是对于所有在 $A_k$ 结束的和为 $S$ 的序列:

它们对答案的贡献为 $(i_1+i_2+\cdots+i_r)\cdot(n-k+1)$.

所以我们想枚举结束点 $k$,计算和为 $S$ 且在 $A_k$ 结束的序列的第一个元素的位置之和,即上式第一个括号的值。考虑 $dp$:

  • dp 状态:$dp[k][s]$ 表示在 $A_k$ 结束的序列的第一个元素的位置之和。
  • 转移方程:$dp[k][s] = \sum\limits_{i=1}^{k-1}dp[i][s-a[k]]$,特别地,$dp[k][a[k]] = k$.
  • 转移顺序:由转移方程可知,顺序枚举即可
  • 边界条件:无

复杂度:$O(NS)$

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;

const int N = 3005;
const LL MOD = 998244353;

int n;
LL S, a[N], dp[N][N], ans, sum[N][N];

int main(){
read(n, S);
for(int i = 1; i <= n; i++) read(a[i]);
for(int k = 1; k <= n; k++){
(dp[k][a[k]] += k) %= MOD;
for(int s = a[k] + 1; s <= S; s++)
(dp[k][s] += sum[k-1][s-a[k]]) %= MOD;
for(int s = 0; s <= S; s++)
sum[k][s] = (sum[k-1][s] + dp[k][s]) % MOD;
}
for(int i = 1; i <= n; i++)
(ans += dp[i][S] * (n - i + 1) % MOD) %= MOD;
printf("%lld\n", ans);
return 0;
}
作者

xyfJASON

发布于

2020-03-22

更新于

2021-02-25

许可协议

评论