「解题报告」ARC138E Decreasing Subsequence

奇妙的转换:看成 \(0,1,\cdots,n\)\(n+1\) 个点的图,然后对于所有 \(a_i \ne 0\)\(i\) 连边 \(i \to a_i - 1\)

\(a_i\) 出现次数最多为 \(1\) 说明所有点入度为 \(1\)\(0\),于是这个图就是由许多条链构成的。

那么对于原序列上的一个下降序列,我们相当于有 \(k\) 条边 \(i_1 \to a_{i_1} - 1,\cdots, i_k \to a_{i_k} - 1\),记 \(r_j = i_j, l_j = a_{i_j} - 1\),那么有 \(l_k < \cdots < l_2 < l_1 < r_1 < r_2 < \cdots < r_k\)。这说明这 \(k\) 条边肯定互不在同一条链里。

那么我们可以考虑将这 \(k\) 条链找出来,对于第 \(i\) 条链,将 \(l_i\) 以左的点加入 \(A\) 集合,将 \(r_i\) 以右的点加入 \(B\) 集合,那么我们其实就是将 \(A,B\) 集合分别划分成 \(k\) 条链,再依次拼接起来,就是上述选择的方案。

我们枚举 \(A,B\) 的集合大小,那么方案数就是以下四个方案数的乘积:

  1. 选出 \(A, B\) 集合的点的方案数(\(\binom{n+1}{|A| + |B|}\)
  2. \(A\) 集合划分为 \(k\) 条链的方案数(\(|A| \brace k\)
  3. \(B\) 集合划分为 \(k\) 条链的方案数(\(|B| \brace k\)
  4. 将剩下的点划分成若干条链的方案数(\(\sum {{n + 1 - |A| - |B|} \brace i}\)

大概 trick 就是将非严格偏序 \(\pm 1\) 变成严格偏序会有更优秀的性质。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5005, P = 1000000007;
int s[MAXN][MAXN], f[MAXN], c[MAXN][MAXN];
int n, k;
int C(int n, int m) {
    if (n < 0 || m < 0 || n < m) return 0;
    return c[n][m];
}
int main() {
    scanf("%d%d", &n, &k);
    n++;
    c[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        c[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
        }
    }
    s[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            s[i][j] = (1ll * j * s[i - 1][j] + s[i - 1][j - 1]) % P;
        }
    }
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= n; j++) {
            f[i] = (f[i] + s[i][j]) % P;
        }
    }
    int ans = 0;
    for (int a = k; a <= n; a++) {
        for (int b = k; a + b <= n; b++) {
            ans = (ans + 1ll * c[n][a + b] * s[a][k] % P * s[b][k] % P * f[n - a - b]) % P;
        }
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2023-01-27 17:54  APJifengc  阅读(65)  评论(0编辑  收藏  举报