题解:P4491 [HAOI2018] 染色

· · 题解

摘要:这里介绍仅使用 EGF 而不使用二项式反演的一种解法。

前置知识:指数生成函数(EGF)、多项式乘法(卷积,NTT)。

简要题意

给定一个长度为 n 的纸带,你需要用最多 m 种颜色给纸带的每一个位置染色。

若有 k 种颜色恰好出现了 S 次,则会获得 W_k 的收益。你需要输出所有染色方案的收益总和。

答案对 1004535809 取模。

## 思路 首先考虑设 $f(z)$ 表示有 $z$ 种颜色恰好出现 $S$ 次的方案数,则答案就是: $$ \sum_{i=0}^{m}W_if(i) $$ 求 $f(z)$ 不妨考虑生成函数。我们将颜色分为恰好出现 $S$ 次的(下面成为关键色)以及恰好出现非 $S$ 次的(下面称为一般色)。由于纸带染色问题属于有标号体系,所以我们需要使用 EGF。 先构造关键色的生成函数: $$ \hat{\mathbf{F}}_1 = \frac{x^S}{S!} $$ 然后是一般色的生成函数: $$ \hat{\mathbf{F}}_0=-\frac{x^S}{S!}+\sum_{k=0}^{+\infty} \frac{x^k}{k!}=e^x-\frac{x^S}{S!} $$ 则有: $$ f(z)=\binom{m}{z}\cdot n!\cdot [x^n]\left(\left(\hat{\mathbf{F}}_1\right)^z\cdot \left(\hat{\mathbf{F}}_0\right)^{m-z}\right) $$ 然后就是大力化式子,先考虑化简后面的多项式: $$ \begin{aligned} &\left(\hat{\mathbf{F}}_1\right)^z\cdot \left(\hat{\mathbf{F}}_0\right)^{m-z}\\ &=\left(\frac{x^S}{S!}\right)^z\cdot \left(e^x-\frac{x^S}{S!}\right)^{m-z}\\ &=\frac{x^{Sz}}{(S!)^z}\cdot \sum_{k=0}^{m-z}\binom{m-z}{k}e^{xk}\frac{x^{Sm-Sz-Sk}}{(S!)^{m-z-k}}(-1)^{m-k-z}\\ &=\sum_{k=0}^{m-z}\binom{m-z}{k}e^{xk}\frac{x^{Sm-Sk}}{(S!)^{m-k}}(-1)^{m-k-z}\\ &=\sum_{k=0}^{m-z}\binom{m-z}{k}\frac{x^{Sm-Sk}}{(S!)^{m-k}}(-1)^{m-k-z}\sum_{t=0}^{+\infty}\frac{k^t}{t!}x^t \end{aligned} $$ 考虑它的 $x^n$ 项系数到底是什么,不难发现需要满足 $S(m-k)+t=n$,也就是 $t=n-S(m-k)$,代入: $$ \begin{aligned} &[x^n]\sum_{k=0}^{m-z}\binom{m-z}{k}\frac{x^{Sm-Sk}}{(S!)^{m-k}}(-1)^{m-k-z}\sum_{t=0}^{+\infty}\frac{k^t}{t!}x^t\\ &=\sum_{k=0}^{m-z}\binom{m-z}{k}\frac{k^{n-S(m-k)}(-1)^{m-k-z}}{(n-S(m-k))!(S!)^{(m-k)}}\\ &=(m-z)!\sum_{k=0}^{m-z}\frac{k^{n-S(m-k)}(-1)^{m-k-z}}{(n-S(m-k))!(S!)^{(m-k)}(m-k-z)!k!} \end{aligned} $$ 换元,令 $k\leftarrow m-k$,得: $$ (m-z)!\sum_{k=z}^{m}\frac{(m-k)^{n-Sk}(-1)^{k-z}}{(n-Sk)!(S!)^{k}(k-z)!(m-k)!} $$ 令: $$ g(k)= \frac{(m-k)^{n-Sk}}{(n-Sk)!(m-k)!(S!)^k} $$ 则得到: $$ \begin{aligned} &(m-z)!\sum_{k=z}^{m}\frac{(m-k)^{n-Sk}(-1)^{k-z}}{(n-Sk)!(S!)^{k}(k-z)!(m-k)!}\\ &=(m-z)!\sum_{k=z}^{m}\frac{(-1)^{k-z}}{(k-z)!}g(k) \end{aligned} $$ 改为枚举 $k-z$ 得: $$ \begin{aligned} &(m-z)!\sum_{k=z}^{m}\frac{(-1)^{k-z}}{(k-z)!}g(k)\\ &=(m-z)!\sum_{k=0}^{m}\frac{(-1)^k}{k!}g(k+z) \end{aligned} $$ 令 $g'(k)=g(m-k)$,得: $$ \begin{aligned} &(m-z)!\sum_{k=0}^{m}\frac{(-1)^k}{k!}g(k+z)\\ &=(m-z)!\sum_{k=0}^{m}\frac{(-1)^k}{k!}g'((m-z)-k) \end{aligned} $$ 最终得到: $$ f(z)=\binom{m}{z}n!(m-z)!\sum_{k=0}^{m}\frac{(-1)^k}{k!}g'((m-z)-k) $$ 注意到后面那个式子可以看成一个卷积形式,答案就是卷积的 $x^{m-z}$ 项系数。因此我们可以构造两个多项式,一遍多项式卷积就可以求出后面那个和式的值。 时间复杂度 $O(n+m\log m)$。 ## 代码 多项式模板过长,已经省略,下面展示核心代码: ```cpp int n, m, s, fact[N], inv[N], w[N]; int binom(int x, int y){ return Mul(fact[x], Mul(inv[y], inv[x - y])); } signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n >> m >> s; for(int i=0;i<=m;i++) cin >> w[i]; fact[0] = fact[1] = inv[0] = inv[1] = 1; for(int i=2;i<=max(n, m);i++){ fact[i] = Mul(fact[i-1], i); inv[i] = Mul(mod - mod / i, inv[mod % i]); } for(int i=2;i<=max(n, m);i++) inv[i] = Mul(inv[i], inv[i-1]); Poly poly(m + 1); for(int k=0;k<=m;k++){ if(n - 1ll * s * k < 0) continue; int tmp = fastpow(m - k, n - 1ll * s * k); tmp = Mul(tmp, inv[n - 1ll * s * k]); tmp = Mul(tmp, inv[m - k]); tmp = Mul(tmp, fastpow(inv[s], k)); poly[k] = tmp; } reverse(poly.a.begin(), poly.a.end()); Poly poly2(m + 1); for(int k=0;k<=m;k++){ int tmp = inv[k]; if(k & 1) tmp = mod - tmp; poly2[k] = tmp; } poly = poly * poly2; int ans = 0; for(int i=0;i<=m;i++){ int tmp = Mul(fact[m - i], fact[n]); tmp = Mul(tmp, poly[m - i]); tmp = Mul(tmp, binom(m, i)); tmp = Mul(tmp, w[i]); ans = Add(ans, tmp); } cout << ans << '\n'; return 0; } // Written by xiezheyuan ```