【题目链接】
【思路要点】
- 笛卡尔树DP,记\(F_{i,j}\)表示长度为\(i\)的区间中所有数的最大值小于等于\(j\),所有方案的贡献之和。
- 考虑枚举区间最大值第一次出现的位置,则有:\(F_{i,j}=F_{i,j-1}+\sum_{pos=1}^{i}w_j^{min(pos,i-k+1)-max(1,pos-k+1)+1}*F_{pos-1,j-1}*F_{i-pos,j}\)。
- 预处理\(w_{i}\)乘幂的结果,时间复杂度\(O(N^3)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 505; const int P = 998244353; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, k, w[MAXN]; long long dp[MAXN][MAXN]; long long power[MAXN][MAXN]; int main() { read(n), read(k); for (int i = 1; i <= n; i++) read(w[i]); for (int i = 0; i <= n; i++) { dp[0][i] = 1; for (int j = 1; j <= n; j++) dp[j][i] = dp[j - 1][i] * i % P; } for (int i = 1; i <= n; i++) { power[i][0] = 1; for (int j = 1; j <= n; j++) power[i][j] = power[i][j - 1] * w[i] % P; } for (int i = k; i <= n; i++) for (int j = 1; j <= n; j++) { long long ans = dp[i][j - 1]; for (int pos = 1; pos <= i; pos++) { int l = max(1, pos - k + 1); int r = min(pos, i - k + 1); ans += power[j][r - l + 1] * dp[pos - 1][j - 1] % P * dp[i - pos][j] % P; } dp[i][j] = ans % P; } writeln(dp[n][n]); return 0; }

本文介绍了一种基于笛卡尔树的动态规划算法,用于解决区间内所有数的最大值小于等于某一阈值的问题。通过枚举区间最大值首次出现的位置,并预处理权重的幂次方结果,最终实现O(N^3)的时间复杂度。
392

被折叠的 条评论
为什么被折叠?



