CF1067A 一个关于数列的有趣问题

1 CF1067A 一个关于数列的有趣问题

2 题目描述

时间限制 \(2s\) | 空间限制 \(512M\)

\(Ivan\) 无意中看到了他曾经的一个生日礼物。一个包含从 \(1\)
\(200\)\(n\) 个数字的数组。由于太过陈旧,有些数字已经看不清楚了。\(Ivan\) 记得,对于所有元素,至少有一个相邻元素不少于它,也就是说 \(𝑎_1≤𝑎_2,𝑎_𝑛≤\)\(𝑎\)\(_𝑛\)\(_−\)\(_1\),同时对于所有的 \(2\)\(n-1\) 都有 \(𝑎_𝑖≤𝑚𝑎𝑥(\)\(𝑎_𝑖\)\(_−\)\(_1\)\(,\)\(𝑎_𝑖\)\(_+\)\(_1\)\()\)
\(Ivan\) 不记得数组了,想找到办法来恢复数组。恢复的元素也应该是从 \(1\)\(200\) 的整数。由于数量可能很大,结果对 \(998244353\) 取模后输出。

3 题解

我们根据题意,可以想到使用 \(dp\)。如果我们 \(dp\) 只定义前 \(i\) 个数中第 \(i\) 个数以 \(j\) 结尾时的最小花费时的方案数,会出现无法转移的问题:因为 \(-1\) 的具体值随着情况的改变而改变,所以我们无法确定 \(-1\) 与当前数的大小关系,进而无法算出答案。因此我们加入另外一维 \(0/1/2\),用来确定当前数与前面那个数的大小关系。

于是,我们就得出了状态:\(dp_{i, j, 0/1/2}\) 表示前 \(i\) 个数中第 \(i\) 个数以 \(j\) 结尾,且 \(j\)\(a_{i-1}\) 的关系为 小于、等于、大于 这种情况总共的方案数。转移分为 \(0、1、2\) 三种状态分别讨论。

\(1. dp_{i, j, 0} = \sum_{k = j+1}^n\limits (dp_{i-1, k, 0} + dp_{i-1, k, 1})\)

\(2.dp_{i, j, 1} = dp_{i-1, j, 0} + dp_{i-1, j, 1} + dp_{i-1, j, 2}\)

\(3.dp_{i, j, 2} = \sum_{k = 1}^n\limits (dp_{i-1, k, 0} + dp_{i-1, k, 1} + dp_{i-1, k, 2})\)

初值为 \(dp_{1, j, 2} = 1\)

我们分条讨论,先分析第一种情况。当 \(j\) 比上一个数小时,上一个数并没有满足小于等于旁边的两个数的最大值这一条件,所以它必须小于等于第 \(i-1\) 个数,也就是只能加情况为 \(0\) 或者 \(1\) 的方案数。由于上一个数严格大于第 \(i\) 个数,所以答案累加了所有第二维大于当前数的 \(dp\) 值。综上所述:\(dp_{i, j, 0} = \sum_{k = j+1}^n\limits (dp_{i-1, k, 0} + dp_{i-1, k, 1})\)

\(j\) 等于上一个数时,上一个数至少满足等于旁边的两个数的最大值这一条件,所以第 \(i-2\) 个数可以随便取值,可以加 \(0\) 或者 \(1\) 或者 \(2\)。然后由于 \(j\) 等于上一个数,所以只能加第二维等于 \(j\) 的方案数。综上所述:\(dp_{i, j, 1} = dp_{i-1, j, 0} + dp_{i-1, j, 1} + dp_{i-1, j, 2}\)

\(j\) 大于上一个数时,上一个数也肯定满足条件,第 \(i-2\) 个数同样可以随便取值,加 \(0\) 或者 \(1\) 或者 \(2\)。由于大于上一个数,加的情况数是第二维小于 \(j\) 的方案数。综上所述:\(dp_{i, j, 2} = \sum_{k = 1}^n\limits (dp_{i-1, k, 0} + dp_{i-1, k, 1} + dp_{i-1, k, 2})\)

我们观察发现,以上第一种情况和第二种情况有累加和这一操作,由于所有的这些需要累加的东西都小于 \(i\) ,我们可以在计算时线性存入前缀和中,注意如果开 \(long \space long\) 的话最好滚掉前缀数组的第一维。

由于\(a_{n} \le a_{n-1}\),我们最终累计答案时只需要加上 \(\sum_{k = 1}^{200}\limits (dp_{n,k,0 } + dp_{n, k, 1})\) 即可。

4 代码(空格警告):

#include <iostream>
using namespace std;
const int N = 1e5+10;
const int M = 205;
const int mod = 998244353;
int n, ans;
int a[N];
int dp[N][M][3];
int sum[M][3];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    if (a[1] == -1)
    {
        for (int i = 1; i <= 200; i++) dp[1][i][2] = 1;
        for (int i = 1; i <= 200; i++) sum[i][2] = (sum[i-1][2] + dp[1][i][2]) % mod;
    }
    else
    {
        dp[1][a[1]][2] = 1;
        for (int i = 1; i <= 200; i++) sum[i][2] = (sum[i-1][2] + dp[1][i][2]) % mod;
    }
    for (int i = 2; i <= n; i++)
    {
        if (a[i] != -1)
        {
            dp[i][a[i]][0] = ((sum[200][0] - sum[a[i]][0] + mod) % mod + (sum[200][1] - sum[a[i]][1] + mod) % mod) % mod;
            dp[i][a[i]][1] = ((dp[i-1][a[i]][0] + dp[i-1][a[i]][1]) % mod + dp[i-1][a[i]][2]) % mod;
            dp[i][a[i]][2] = ((sum[a[i]-1][0] % mod + sum[a[i]-1][1] % mod) % mod + sum[a[i]-1][2] % mod) % mod;
            for (int j = 1; j <= 200; j++) sum[j][0] = (sum[j-1][0] + dp[i][j][0]) % mod;
            for (int j = 1; j <= 200; j++) sum[j][1] = (sum[j-1][1] + dp[i][j][1]) % mod;
            for (int j = 1; j <= 200; j++) sum[j][2] = (sum[j-1][2] + dp[i][j][2]) % mod;
        }
        else
        {
            for (int j = 1; j <= 200; j++)
            {
                dp[i][j][0] = ((sum[200][0] - sum[j][0] + mod) % mod + (sum[200][1] - sum[j][1] + mod) % mod) % mod;
                dp[i][j][1] = ((dp[i-1][j][0] + dp[i-1][j][1]) % mod + dp[i-1][j][2]) % mod;
                dp[i][j][2] = ((sum[j-1][0] % mod + sum[j-1][1] % mod) % mod + sum[j-1][2] % mod) % mod;
            }
            for (int j = 1; j <= 200; j++)
            {
                sum[j][0] = (sum[j-1][0] + dp[i][j][0]) % mod;
                sum[j][1] = (sum[j-1][1] + dp[i][j][1]) % mod;
                sum[j][2] = (sum[j-1][2] + dp[i][j][2]) % mod;
            }
        }
    }
    for (int i = 1; i <= 200; i++)
    {
        ans = (ans + ((dp[n][i][0] + dp[n][i][1]) % mod)) % mod;
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-05 16:13  David24  阅读(84)  评论(0编辑  收藏  举报