CF1542D Priority Queue 题解

一、题目:

洛谷原题

codeforces原题

二、思路:

这道题很明显是一道 DP 题。

考虑计算某个 + x 对答案的贡献。也就是说,我们的目标是对于每个 + x,求出有多少个子序列满足经过一番操作之后,这个 \(x\) 会被统计到答案中。

对于固定的、处于位置 \(p\) 的一个 + x,考虑 DP。设 \(dp[i,j]\) 表示考虑了前 \(i\) 位,有 \(j\) 个比 \(x\) 小的方案数。

  1. 若第 \(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\) 没有影响。

  2. 若第 \(i\) 位是正数而且比 \(x\) 大,则 \(dp[i,j]=dp[i-1,j]\times 2\)。选和不选都不会使得 \(j\) 改变。

  3. 若第 \(i\) 位是正数而且比 \(x\) 小,则

    \[dp[i,j]= \begin{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\n", ans);
    return 0;
}
posted @ 2021-07-04 10:46  蓝田日暖玉生烟  阅读(102)  评论(0编辑  收藏  举报