1067A - Array Without Local Maximums

题目大意

  有一串长度为$n$的数字,其中由一些数字看不清了($-1$的数字就是看不清的),而这些数字取值范围满足$0\leq a \leq20 $,问满足一下条件

$(1) a_{1}\leq a_{2}$

$(2) a_{n}\leq a_{n-1}$

$(3) a_{i}\leq max(a_{i-1},a_{i+1}) i\in [2,n-1]$

的方案数有多少种.

分析

  dp。$dp[i][num][flag]=$第$i$个数字为$num$,$(1) flag=0$时,表示$a_{i-1}< a_{i}$,$(2) flag=1$时,表示$a_{i-1}\geq a_{i}$的方案数。可能有人会有点疑问,为什么这样定义,看下面几张图

根据条件,连续的3个点是互相关联的,如果我们将这三个连续的写为$A,B,C$如果想让$C$合法,由上面的图上我们得到由$B\geq C且A\geq C$和$C>B$这样两种情况得到,然后再根据$flag$的定义,我们可以得到以下状态转移方程$dp[i][num][0]=\sum_{j=1}^{num-1} (dp[i-1][j][1]+dp[i-1][j][0])$和$dp[i][num][1]=dp[i][num][0]+\sum_{j=num}^{200} {dp[i-1][j][1]}$,这里$num$的两种情况的转移方程并不差别。但是如果这样做复杂度是$O_{(n\cdot num \cdot num)}$我们可以通过前缀和和后缀和,将复杂度变成$O_{(n\cdot num)}$详细看代码

#define frp

#include<bits/stdc++.h>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstring>
#include <string>
#include <string.h>
#include <iomanip>

using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const ll inf = 0x7fffff;
const int maxn = 2e6;
const int MAXN = 1100000 + 10;
const int MOD = 1e9 + 7;
const ll mod = 998244353; 
ll dp[100005][210][2];
ll a[maxn];
int n;

void solve() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i + 1];
    }
    if (a[1] != -1) {
        dp[1][a[1]][0] = 1;
    } else {
        for (int i = 1; i < 201; ++i) {
            dp[1][i][0] = 1;
        }
    }
    for (int i = 2; i < n + 1; ++i) {
        if (a[i] == -1) {//如果是看不清的数字
            ll sum = 0;
            for (int j = 1; j < 201; ++j) {//a[i-1]<a[i]的情况,因为1是不可能比其他数字大的,所以是求前缀和
                dp[i][j][0] = sum;
                sum = (sum + dp[i - 1][j][0] + dp[i - 1][j][1]) % mod;
            }
            sum = 0;
            for (int j = 200; j > 0; --j) {//从后往前的原因是a[i-1]>=a[i],a[i-2]必须大于a[i-1],则a[i-2]必定大于a[i],如果从前往后,那么a[i-2]是不一定大于a[i]的,有点像01背包二维变一维时的思路
                sum = (sum + dp[i - 1][j][1]) % mod;//后缀和
                dp[i][j][1] = (sum + dp[i - 1][j][0]) % mod;
            }
        } else {
            for (int j = 1; j < a[i]; ++j) {
                dp[i][a[i]][0] = (dp[i][a[i]][0] + dp[i - 1][j][0] + dp[i - 1][j][1]) % mod;
            }
            for (int j = 200; j >= a[i]; --j) {
                dp[i][a[i]][1] = (dp[i][a[i]][1] + dp[i - 1][j][1]) % mod;
            }
            //这条不要忘了
            dp[i][a[i]][1] = (dp[i][a[i]][1] + dp[i - 1][a[i]][0]) % mod;
        }
    }
    if (a[n] != -1) {//如果第n个是看得清的数字,直接输出
        cout << dp[n][a[n]][1] << endl;
    } else {//如果是看的不清的数字,将1-200之间可能的方案数相加,即为所求的答案
        ll ans = 0;
        for (int i = 1; i < 201; ++i) {
            ans = (ans + dp[n][i][1]) % mod;
        }
        cout << ans << endl;
    }
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
#ifdef frp
    freopen("D:\\coding\\c_coding\\in.txt", "r", stdin);
//    freopen("D:\\coding\\c_coding\\out.txt", "w", stdout);
#endif
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

  

posted @ 2018-11-11 15:55  visualVK  阅读(213)  评论(0编辑  收藏  举报