「解题报告」CF1528F AmShZ Farm

发布时间 2023-04-14 09:51:16作者: APJifengc

之前 zzy 讲题讲到的,今天在题单里看到了,就又做了下。

首先发现,对于一个 \(a\) 数组,\(b\) 数组的方案数就是 \(a\) 中每个数的出现次数的 \(k\) 次方加和。

考虑如何将 \(a\) 数组的条件转化一下。贪心的想,对于一个 \(a_i\),我们肯定要尽可能使得它最终的数尽可能小。那么我们考虑每次将 \(a_i\) 加至第一个没有被加过的数,如果没有这样的数那么显然就不合法。

这样考虑仍不好考虑,我们假设将值域扩大 \(1\),这样不合法的条件就变为了,如果最终的序列中出现了 \(n+1\),那么序列不合法。此时我们可以在模 \(n+1\) 意义下做这件事情,这样上述贪心一定有解,而不合法条件不变。这时候我们发现,对于任意一个值域为 \([1, n + 1]\),长度为 \(n\) 的序列来说,最后一定恰好剩下一个数没有被选。那么我们可以将值域平移至没有被选的那个位置,这样一定能构造出一组合法序列。这意味着,每一种合法序列对应着 \(n+1\) 种序列,这样我们可以考虑所有序列下的答案,最后除以 \(n+1\) 就是原问题的答案。

考虑任意序列下怎么做。首先显然每个数的贡献是独立的,所以我们可以考虑只求一个数的出现次数的 \(k\) 次方,最后乘 \(n+1\) 就是所有数的答案。那么容易列出答案式子为 \(\sum_{i=0}^n \binom{n}{i} n^{n - i} i^k\)

这个求和上标为 \(n\),肯定不好做,后面有一个 \(k\) 次幂,容易想到拆成下降幂,那么推式子:

\[\begin{aligned} &\sum_{i=0}^n \binom{n}{i} n^{n - i} i^k\\ =&\sum_{i=0}^n \binom{n}{i} n^{n - i} \sum_{j=0}^k {k \brace j} \binom{i}{j} j!\\ =&\sum_{j=0}^k {k \brace j} j!\sum_{i=j}^n \binom{n}{i} \binom{i}{j} n^{n - i} \\ =&\sum_{j=0}^k {k \brace j} j!\sum_{i=j}^n \binom{n}{j} \binom{n - j}{i - j} n^{n - i} \\ =&\sum_{j=0}^k {k \brace j} j! \binom{n}{j} \sum_{i=j}^n \binom{n - j}{i - j} n^{n - i} \\ =&\sum_{j=0}^k {k \brace j} j! \binom{n}{j} \sum_{i=0}^{n-j} \binom{n - j}{i} n^{n - j - i} \\ =&\sum_{j=0}^k {k \brace j} n^{\underline{j}} (n+1)^{n-j}\\ \end{aligned} \]

那么只需要 NTT 求出一行斯特林数,然后剩下的直接算即可。

int main() {
    scanf("%d%d", &n, &k);
    init(k);
    Polynomial a(k), b(k);
    for (int i = 0; i <= k; i++) {
        a[i] = 1ll * qpow(i, k) * inv[i] % P;
        b[i] = ((i & 1) ? P - 1ll : 1ll) * inv[i] % P; 
    }
    Polynomial s = a * b;
    int ans = 0, tmp = 1;
    for (int i = 0; i <= min(k, n); i++) {
        ans = (ans + 1ll * s[i] * tmp % P * qpow(n + 1, n - i)) % P;
        tmp = 1ll * tmp * (n - i) % P;
    }
    printf("%d\n", ans);
    return 0;
}