C Easy Counting Problem
C 题题意:给定字符集大小 Σ,和每个字符出现次数的下限 {ci},q 次询问由 Σ 中字符构成的长度为 n 的字符串的个数。q≤300,n≤1×107,∑ci≤5×104,∣Σ∣≤10。
解法:对于 ∑ci 较小的约束条件,可以考虑使用生成函数。本题中由于有次数限制并且对排列计数,因而使用 EGF 指数生成函数。对于第 i 个字符,其方案对应的 EGF 为
j=ci∑+∞j!xj=ex−j=0∑ci−1j!xj
因而对于给定长度的 n,其答案为 n![xn]i=1∏∣Σ∣j=ci∑+∞j!xj。考虑如何快速计算:
i=1∏∣Σ∣j=ci∑+∞j!xj==i=1∏∣Σ∣(ex−j=0∑ci−1j!xj)i=0∑∣Σ∣(−1)∣Σ∣−ieix⎝⎛j∈S,∣S∣=∣Σ∣−i∏k=0∑cj−1k!xk⎠⎞
其中 S∈Σ。但是直接计算上式是需要计算 O(∣Σ∣2∣Σ∣) 次多项式相乘,不够优秀。但是注意到这一形式与 01 背包非常相似,因而使用背包形式的转移:fi,j 表示截止前 i 个字符,已经乘了 j 个多项式的多项式。其转移形式为 fi,j←fi−1,j+fi−1,j−1g(j),其中 g(j)=k=0∑cj−1k!xk。 这样只需要计算 O(∣Σ∣2) 次多项式乘法计算。
因而预处理出 f∣Σ∣,i,i∈[0,∣Σ∣] 后,容易注意到 f∣Σ∣,i 的次数仅为 ∑ci。对于第 n 项,其答案来源为 e(∣Σ∣−i)x 的第 n−k 项与 f∣Σ∣,i 的第 k 项的系数乘积和,即 k=0∑∑cii=0∑∣Σ∣(−1)∣Σ∣−i(n−k)!n!(∣Σ∣−i)n−k([xk]f∣Σ∣,i)。
因而总的复杂度为 O(∣Σ∣2∑cilog∑ci+q∣Σ∣∑ci)。
void Solve() {
scanf("%d", &m);
vector<Poly> p(m + 1);
p[0] = {1};
for (int i = 0, x; i < m; ++i) {
scanf("%d", &x);
Poly c = Poly(ifac, ifac + x);
for (int j = i + 1; j; --j)
p[j] += p[j - 1] * c;
}
scanf("%d", &q);
while (q--) {
int n, ans = 0;
scanf("%d", &n);
if (n < p[m].size()) {
puts("0");
continue;
}
for (int k = 0; k <= m; ++k) {
int res = 0;
for (int i = (int)p[k].size() - 1, pw = POW(m - k, n - i); ~i; --i) {
res = (res + (ll)p[k][i] * pw % P * ifac[n - i]) % P;
pw = (ll)pw * (m - k) % P;
}
inc(ans, k & 1 ? P - res : res);
}
printf("%lld\n", (ll)ans * fac[n] % P);
}
}