题目传送门
Step1 理解题意
- 给定一个 n n n 和一个 s s s
- 给定一个长度为 n n n 的正整数数组 a a a
- 定义函数 f ( L , R ) f(L,R) f(L,R) 是指在 L L L 到 R R R 的区间内能使其中子串(子串是不需要连续的)之和为 s s s 的子串数量
- 求在 a a a 中所有 f ( L , R ) f(L,R) f(L,R) 之和
- 结果对 998244353 998244353 998244353 取模
-
1
≤
N
,
S
,
A
i
≤
3000
1 \leq N,S,A_i \leq 3000
1≤N,S,Ai≤3000
Step2 思路解释
让
d
p
i
,
j
dp_{i,j}
dpi,j指在前
i
i
i 个数的总和为
j
j
j,那么我们易得以下转移方程:
d
p
i
,
j
←
(
d
p
i
−
1
,
j
+
d
p
i
−
1
,
j
−
a
i
)
m
o
d
p
dp_{ i,j} \gets (dp_{i−1,j}+dp_{ i−1,j−a _i }) \mod p
dpi,j←(dpi−1,j+dpi−1,j−ai)modp
Step3 简化数组
- 因为每次枚举 d p i , j dp_{ i , j } dpi,j都只要用到 d p i − 1 , x dp_{ i -1 , x} dpi−1,x,所以我们可以省去一维,即用 d p i dp_i dpi 表示 d p i , j dp_{i,j} dpi,j。
- 如果只考虑 A i = S A_i = S Ai=S 的情况,则会少算 i i i 种右端点为 i i i 的方案,所以对于每个 i i i,应该使 d p 0 ← i dp_0 \gets i dp0←i。
Step4 Ac code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int a[3005],dp[3005];
int ans;
signed main(){
int n ,s;
scanf("%lld%lld",&n,&s);
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
for(int i = 1; i <= n ;i++){
dp[0]++;
for(int j = s; j >= a[i]; j--){
dp[j] = (dp[j] + dp[j - a[i]]) % mod;
}
ans += dp[s];
ans %= mod;
}
printf("%lld",ans);
return 0;
}