[dp][AGC024E] Sequence Growing Hard
大概又是一道比较套路的 \(dp\) (?
从前往后考虑序列,若将数 \(c\) 插到 \(s_i\) 的位置,那么必须满足 \(c+s_{i..|s|}\) 字典序大于 \(s_{i..|s|}\)。
-
若 \(c > s_i\),显然合法;
-
若 \(c = s_i\),则 \(c\) 必须大于 \(s_{i+1..|s|}\) 中第一个和 \(c\) 不同的数。
显然第一类可以归到第二类去。
考虑从小到大插入 \([1,k]\),记 \(dp(i,j,k)\) 为从前往后考虑到第 \(i\) 个序列,填入的数值域为 \([1,j]\),有 \(k\) 个位置可以插入数(设为分成 \(k\) 段不好转移)的方案数。
转移:
-
\(dp(i,j,k) * k \longrightarrow dp(i+1,j,k)\) (加入一个 \(j\))
-
\(dp(i,j,k) \longrightarrow dp(i,j,k-1)\) (\(k > 1\),去掉一个可插入位置)
-
\(dp(i,j,k) \longrightarrow dp(i,j+1,i)\) (\(k = 1\),考虑下一个数)
答案为 \(dp(n,k,1)\) 。
\(\texttt{Code:}\)
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline void read(int &x) {
x = 0; int f = 0; char c = getchar();
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
x = f ? -x : x;
}
const int N = 300 + 5;
int n, k, cmd, dp[N][N][N];
int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}
int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}
int main() {
read(n); read(k); read(cmd);
dp[0][1][1] = 1;
for (int i = 0; i <= n; i++)
for (int j = 1; j <= k; j++)
for (int x = i + 1; x >= 1; x--) {
if (!dp[i][j][x]) continue;
dp[i + 1][j][x] = (dp[i + 1][j][x] + 1ll * dp[i][j][x] * x) % cmd;
if (x > 1) dp[i][j][x - 1] = add(dp[i][j][x - 1], dp[i][j][x]);
else dp[i][j + 1][i + 1] = add(dp[i][j + 1][i + 1], dp[i][j][x]);
}
printf("%d", dp[n][k][1]);
return 0;
}

浙公网安备 33010602011771号