[dp][AGC024E] Sequence Growing Hard

\(\texttt{link}\)

大概又是一道比较套路的 \(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;
}
posted @ 2021-11-27 15:14  klii  阅读(32)  评论(0)    收藏  举报