神秘二项式反演 + EGF 推导题目。
题意:有 $n$ 个 $[1, D]$ 的随机变量,求至少能组成 $m$ 个相同数字的二元组的方案数。$n, m\leq 10 ^ 9$,$ D\leq 10 ^ 5$。
显然可以计算出现次数为奇数的 $\leq n - 2\times m$ 个。
首先特判 $2\times m>n$ 和 $2\times m\leq n - D$ 的情况,答案分别为 0 和 $D ^ n$。
容易发现我们需要对整个序列使用 EGF。我们知道 $\{a, a ^ 2, a ^ 3, \cdots\}$ 的 EGF 是 $e ^ {ax}$,那么 $\{1,1, 1, \cdots\}$ 的 EGF 是 $e ^ x$,$\{1, -1, 1, -1\}$ 的 EGF 是 $e ^ {-x}$,那么我们只要奇数的,那么就是 $\dfrac{e ^ x - e ^ {-x}}{2}$。
那么我们现在需要计算 $\forall k\in [1, n - 2\times m]$ 的答案,记为 $f(k)$。
这个是一个恰好的形式,然后考虑二项式反演,计算钦定 $k$ 个为奇数的答案,记为 $g(k)$。
按照刚才的 EGF,我们可以写出 $g(k)$ 的生成函数:
最后一步是二项式定理展开。然后我们就可以得到 $g(k)$ 的表达式:
于是可以卷积计算 $g(k)$,时间复杂度 $O(D\log D)$。
然后按照 Luogu P4491 染色 的套路,容易把二项式反演的过程卷积加速,于是总复杂度 $O(D\log D)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| int main() { init(); int D, n, m; std::cin >> D >> n >> m;
if (2 * m > n) return puts("0"), 0;
if (2 * m <= n - D) return printf("%d\n", qpow(D, n)), 0;
poly a(D + 1), b(infact, infact + D + 1);
for (int i = 0, op = 1, tmp; i <= D; ++ i, op = Mod - op) a[i] = qpow(adj(tmp = D - 2 * i), n) * (LL) infact[i] % Mod * op % Mod;
a = a * b, a.resize(D + 1);
for (int i = 0; i <= D; ++ i) a[i] = (LL) a[i] * fact[D] % Mod * infact[D - i] % Mod * qpow(2, Mod - 1 - i) % Mod;
for (int i = 0; i <= D; ++ i) a[i] = (LL) a[i] * fact[i] % Mod;
for (int i = 0; i <= D; ++ i) if (i & 1) adj(b[i] = -b[i]);
std::reverse(b.begin(), b.end()); a = a * b;
for (int i = 0; i <= D; ++ i) a[i + D] = (LL) a[i + D] * infact[i] % Mod;
int res = 0;
for (int i = 0; i <= n - 2 * m; ++ i) adj(res += a[i + D] - Mod);
std::cout << res << std::endl; return 0; }
|