【CTS 2019】珍珠
Problem
Description
有 \(n\) 个在范围 \([1,D]\) 内的整数均匀随机变量。
求至少能选出 \(m\) 个瓶子,使得存在一种方案,选择一些变量,并把选出来的每一个变量放到一个瓶子中,满足每个瓶子都恰好装两个值相同的变量的概率。
请输出概率乘上 \(D^n\) 后对 \(998244353\) 取模的值。取模部分说明可参考 。
Range
\(0\le m\le 10^9,1\le n\le 10^9,1\le D\le 10^5\) 。
Algorithm
\(FFT\) ,生成函数
Mentality
对于 \(2m>n\) 和 \(n-2m \ge D\) 的情况先特判掉。
不难发现,设 \(a_i\) 为颜色 \(i\) 出现的次数,我们要求的就是能满足 \(\sum a_i\ mod\ 2 \le n - 2m\) 的方案数。
设 \(f_i\) 为恰好有 \(i\) 种颜色出现次数为奇数的方案数,则有:
写出 \(f_i\) 的表达式之后不太好求,我们需要令限制更少一点。
考虑钦定 \(i\) 个颜色的出现次数为奇数,设 \(h_i\) 为,对于钦定的某 \(i\) 种颜色,使得出现次数为奇数,且其他颜色出现次数随意的方案数。
设 \(g_i = \tbinom{D}{i} h_i\) ,也就是对于每种不同的钦定方案,我们将其求和。
那么根据定义,必然有等式:
因为每个 \(f_j\) 都会被 \(g_i\) 中的 \(\tbinom{j}{i}\) 种钦定方案算到。
利用指数形生成函数 \(\sum c^n\frac{x^i}{i!} = e^{cx}\) 可得:
随后因为 \([x^n]e^{cx}=\frac{c^n}{n!}\) ,直接代入得:
这样就可以卷积了。
然后根据定义式直接算出 \(\{g_i\}\) 。
根据二项式反演可得:
设 \(p_{D-j-i}=g_{j+i}(j+i)!\),则有:
这样就可以卷积了。
Code
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
#define go(G, x, i, v) \
for (int i = G.hd[x], v = G.to[i]; i; v = G.to[i = G.nx[i]])
#define inline __inline__ __attribute__((always_inline))
inline LL read() {
LL x = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') w = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * w;
}
const int Max_n = 8e5 + 5, mod = 998244353;
int D, n, m;
int fac[Max_n], ifac[Max_n];
int f[Max_n], g[Max_n], G[Max_n];
int ksm(int a, int b = mod - 2) {
int res = 1;
for (; b; b >>= 1, a = (LL)a * a % mod)
if (b & 1) res = (LL)res * a % mod;
return res;
}
namespace Poly {
int len, bit, rev[Max_n];
void init(int n) {
len = 1 << (bit = log2(n) + 1);
for (int i = 0; i < len; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
}
void dft(int *f, bool t) {
for (int i = 0; i < len; i++)
if (rev[i] > i) swap(f[i], f[rev[i]]);
for (int l = 1; l < len; l <<= 1) {
int Wn = ksm(3, (mod - 1) / (l << 1));
if (t) Wn = ksm(Wn);
for (int i = 0; i < len; i += l << 1) {
int Wnk = 1;
for (int j = i; j < i + l; j++, Wnk = (LL)Wnk * Wn % mod) {
int x = f[j], y = (LL)f[j + l] * Wnk % mod;
f[j] = (x + y) % mod, f[j + l] = (x - y + mod) % mod;
}
}
}
if (t)
for (int i = 0, Inv = ksm(len); i < len; i++) f[i] = (LL)f[i] * Inv % mod;
}
void Mul(int *f, int *g, int N) {
init(N);
dft(f, 0), dft(g, 0);
for (int i = 0; i < len; i++) f[i] = (LL)f[i] * g[i] % mod;
dft(f, 1), dft(g, 1);
}
} // namespace Poly
using namespace Poly;
namespace Input {
void main() { D = read(), n = read(), m = read(); }
} // namespace Input
namespace Init {
void main() {
fac[0] = 1;
for (int i = 1; i <= D; i++) fac[i] = (LL)fac[i - 1] * i % mod;
ifac[D] = ksm(fac[D]);
for (int i = D; i; i--) ifac[i - 1] = (LL)ifac[i] * i % mod;
}
} // namespace Init
namespace Solve {
int C(int n, int m) {
if (n < m || m < 0) return 0;
return (LL)fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
void main() {
if (2 * m > n) {
puts("0");
return;
}
if (D <= n - 2 * m) {
printf("%d\n", ksm(D, n));
return;
}
for (int i = 0, t = 1; i <= D; i++, t *= -1) {
G[i] = ifac[i];
g[i] =
(LL)(t + mod) * ksm((D - 2 * i + mod) % mod, n) % mod * ifac[i] % mod;
}
Mul(G, g, D + 1 << 1);
for (int i = 0; i <= D; i++) G[i] = (LL)ksm(ksm(2, i)) * G[i] % mod * fac[i] % mod * C(D, i) % mod;
for (int i = 0, t = 1; i <= D; i++, t *= -1) {
g[i] = (LL)G[D - i] * fac[D - i] % mod;
f[i] = (LL)(t + mod) * ifac[i] % mod;
}
Mul(f, g, D + 1 << 1);
int ans = 0;
for (int i = 0; i <= n - 2 * m; i++)
(ans += (LL)f[D - i] * ifac[i] % mod) %= mod;
cout << ans << endl;
}
} // namespace Solve
int main() {
#ifndef ONLINE_JUDGE
freopen("3120.in", "r", stdin);
freopen("3120.out", "w", stdout);
#endif
Input::main();
Init::main();
Solve::main();
}