「解题报告」CF1528F AmShZ Farm
之前 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;
}