一、题目:
二、思路:
这道题很明显是一道 DP 题。
考虑计算某个 + x
对答案的贡献。也就是说,我们的目标是对于每个 + x
,求出有多少个子序列满足经过一番操作之后,这个 (x) 会被统计到答案中。
对于固定的、处于位置 (p) 的一个 + x
,考虑 DP。设 (dp[i,j]) 表示考虑了前 (i) 位,有 (j) 个比 (x) 小的方案数。
-
若第 (i) 位是
-
,则 (dp[i,j]=dp[i-1,j]+dp[i-1,j+1])。这里需要着重强调,如果 (i<p) 且 (j=0),那么 (dp[i,0]) 还要再加上 (dp[i-1,0])。原因是这样的,如果 (i<p),那么根据题目的约定,如果此时 (T) 是空的,那么我们什么也不做,所以,此时选上
-
号不会使得 (j) 变小。如果此时 (T) 中还有比 (x) 大的数,那-
号也只会消去比 (x) 大的数,同样对 (j) 没有影响。 -
若第 (i) 位是正数而且比 (x) 大,则 (dp[i,j]=dp[i-1,j] imes 2)。选和不选都不会使得 (j) 改变。
-
若第 (i) 位是正数而且比 (x) 小,则
[dp[i,j]= egin{cases} dp[i-1,j] &j=0\ dp[i-1,j]+dp[i-1,j-1] &j>0 end{cases} ]
细心的同学可能会发现,那第 (i) 位如果和 (x) 相等怎么办?
我的处理方法是这样的,为了避免重复计算,强制规定,如果 (i<p),那么就把它归到第 3 种情况中;如果 (i>p),那么就把它归到第 2 种情况中。
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 505, MOD = 998244353;
int n, a[MAXN];
long long dp[MAXN][MAXN];
int main() {
n = read();
for (int i = 1; i <= n; ++ i) {
char op[3]; scanf("%s", op);
if (*op == '-')
a[i] = -1;
else
a[i] = read();
}
long long ans = 0;
for (int p = 1; p <= n; ++ p) {
if (a[p] < 0) continue;
int x = a[p];
mem(dp, 0); dp[0][0] = 1;
for (int i = 1; i <= n; ++ i) {
if (i == p) {
memcpy(dp[i], dp[i - 1], sizeof dp[i]);
continue;
}
for (int j = 0; j <= n; ++ j) {
if (a[i] < 0) {
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j + 1]) % MOD;
if (j == 0 && i < p) (dp[i][j] += dp[i - 1][j]) %= MOD;
}
else if (a[i] < x || (a[i] == x && i < p)){
dp[i][j] = dp[i - 1][j];
if (j > 0) (dp[i][j] += dp[i - 1][j - 1]) %= MOD;
}
else
dp[i][j] = 2 * dp[i - 1][j] % MOD;
}
}
long long sum = 0;
for (int j = 0; j <= n; ++ j)
(sum += dp[n][j]) %= MOD;
(ans += sum * x % MOD) %= MOD;
}
printf("%lld
", ans);
return 0;
}