LGR199D(P11079) 题解
Register_int · · 题解
先对
- 每个山峰至少要有一个上坡。
- 若出现了 V 字形的峡谷,那么同一个子序列会对应两种划分成山峦的方案。
解决方法是添加一个起始点来区分。设
转移需要枚举上一个,复杂度为
AC 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e2 + 10;
const int mod = 998244353;
inline void add(int &x, int y) { x += y, x < mod || (x -= mod); }
int n, m, a[MAXN], b[MAXN], dp[MAXN][MAXN][4], ans;
// dp_i,j,0:新开一个向上单点(只能从下坡转移)
// dp_i,j,1:上坡
// dp_i,j,2:新开一个向下单点
// dp_i,j,3:下坡
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
sort(b + 1, b + n + 1), m = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
for (int i = 1; i <= n; i++) {
dp[i][0][0] = 1;
for (int j = 1; j < i; j++) {
for (int k = 0; k <= m; k++) {
add(dp[i][k][0], dp[j][k][2]);
if (a[i] <= a[j]) add(dp[i][k][0], dp[j][k][3]);
}
}
for (int j = 1; j < i; j++) {
if (a[j] >= a[i]) continue;
for (int k = 0; k <= m; k++) {
add(dp[i][k][1], dp[j][k][0]);
add(dp[i][k][1], dp[j][k][1]);
}
}
for (int j = 1; j < i; j++) {
if (a[j] <= a[i]) continue;
for (int k = 0; k < a[j]; k++) {
add(dp[i][a[j]][2], dp[j][k][1]);
}
}
for (int j = 1; j < i; j++) {
if (a[j] <= a[i]) continue;
for (int k = 0; k <= m; k++) {
add(dp[i][k][3], dp[j][k][2]);
add(dp[i][k][3], dp[j][k][3]);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
add(ans, dp[i][j][2]);
add(ans, dp[i][j][3]);
}
}
printf("%d", ans);
}