题目大意
有一串长度为$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;
}