题目描述
难度分:1700
输入T(≤104)表示T组数据。所有数据的n之和≤3×105。
每组数据输入n(2≤n≤2×105)和长为n的数组a(−109≤a[i]≤109)。
初始化s=0,然后从左到右遍历a。对于每个a[i],你有两个选项:
- 把s更新为s+a[i]。
- 把s更新为|s+a[i]|。
设s的最大值为k。输出使s=k的方案数,模998244353。
两个选项得到的结果即使是一样的,也算不同的方案。
输入样例
5
4
2 -5 3 -3
8
1 4 3 4 1 4 3 4
3
-1 -2 -3
4
-1000000000 1000000000 1000000000 1000000000
4
1 9 8 4
输出样例
12
256
1
8
16
算法
动态规划
状态定义
f[i]表示从1操作到i能够得到的最小值,g[i]表示从1操作到i能够得到的最大值。dp1[i]、dp2[i]分别表示能够得到f[i]、g[i]的方案数。在这个状态定义下,答案就应该是dp2[n]。
状态转移
如果一路不加绝对值,得到的肯定是最小值,也就是说f[i]=f[i−1]+a[i]。但是当f[i−1]<0时,在i位置加上绝对值有可能会超过g[i−1]+a[i],因此g[i]=max(|f[i−1]+a[i]|,|g[i−1]+a[i]|)。这就像求连乘的最大值一样,需要同时保留前面的最大值和最小值,防止负负得正。
如果f[i−1]+a[i]<0,那肯定是在i时不取绝对值能够得到最小值,有状态转移dp1[i]=dp1[i−1]。否则在i时可以取绝对值也可以不取绝对值,状态转移方程为dp1[i]=2×dp1[i−1]。
下面考虑dp2[i]怎么转移。分为以下几种情况:
- |f[i−1]+a[i]|<|g[i−1]+a[i]|,g[i]由g[i−1]得来。如果g[i−1]+a[i]<0,那就必须要在i处取绝对值才能得到g[i],状态转移方程为dp2[i]=dp2[i−1]。如果g[i−1]+a[i]≥0,那i处取不取绝对值都能得到最大值,状态转移方程为dp2[i]=2×dp2[i−1]。
- |f[i−1]+a[i]|>|g[i−1]+a[i]|,g[i]由f[i−1]得来。如果f[i−1]+a[i]<0,那就必须要在i处取绝对值才能得到g[i],状态转移方程为dp2[i]=dp1[i−1]。如果g[i−1]+a[i]≥0,那i处取不取绝对值都能得到最大值,状态转移方程为dp2[i]=2×dp1[i−1]。
- |f[i−1]+a[i]|=|g[i−1]+a[i]|,f[i−1]和g[i−1]都能得到g[i]。如果f[i−1]≠g[i−1],就综合以上两种情况。否则,以上两种情况就是重复的,随便选一个做转移就行。
复杂度分析
时间复杂度
状态数量是O(n)的,单次转移是O(1)的,遍历一遍数组a就可以求得方案数。因此,整个算法的时间复杂度为O(n)。
空间复杂度
四个DP
矩阵f、g、dp1、dp2,空间都是线性的。因此,整个算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200010, MOD = 998244353;
int T, n, a[N], dp1[N], dp2[N];
LL f[N], g[N];
int main() {
scanf("%d", &T);
for(int cases = 1; cases <= T; cases++) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
f[i] = g[i] = dp1[i] = dp2[i] = 0;
}
f[0] = g[0] = 0;
dp1[0] = dp2[0] = 1;
for(int i = 1; i <= n; i++) {
f[i] = f[i - 1] + a[i];
LL v1 = abs(g[i - 1] + a[i]), v2 = abs(f[i - 1] + a[i]);
g[i] = max(v1, v2);
dp1[i] = (dp1[i] + dp1[i - 1]) % MOD;
if(f[i - 1] + a[i] >= 0) {
dp1[i] = (dp1[i] + dp1[i - 1]) % MOD;
}
if(v1 > v2) {
dp2[i] = (dp2[i] + dp2[i - 1]) % MOD;
if(g[i - 1] + a[i] >= 0) {
dp2[i] = (dp2[i] + dp2[i - 1]) % MOD;
}
}else if(v1 < v2) {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
if(f[i - 1] + a[i] >= 0) {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
}
}else {
if(f[i - 1] == g[i - 1]) {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
if(f[i - 1] + a[i] >= 0) {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
}
}else {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
if(f[i - 1] + a[i] >= 0) {
dp2[i] = (dp2[i] + dp1[i - 1]) % MOD;
}
dp2[i] = (dp2[i] + dp2[i - 1]) % MOD;
if(g[i - 1] + a[i] >= 0) {
dp2[i] = (dp2[i] + dp2[i - 1]) % MOD;
}
}
}
}
printf("%d\n", dp2[n]);
}
return 0;
}