洛谷P5401 [CTS2019]珍珠(生成函数)
题目大意
白云苍狗,沧海桑田。
白云的眼前只剩下了模糊的一片。
在若隐若现之中,它看到了一个个小小的珍珠,有一些发着五彩的光芒。这些珍珠是白兔留下来的,每颗珍珠有一个颜色,为 \(D\) 种颜色中随机的一种。
白云想把这些珍珠放进一些小瓶子中,每个瓶子能恰好容纳两颗珍珠。不过它也有要求,每个瓶子必须装满,并且装的都是相同颜色的珍珠。
白云希望能得到至少 \(m\) 个装满珍珠的瓶子,它想知道它的愿望能被实现的概率是多少呢?
有 \(n\) 个在范围 \([1,D]\) 内的整数均匀随机变量。
求至少能选出 \(m\) 个瓶子,使得存在一种方案,选择一些变量,并把选出来的每一个变量放到一个瓶子中,满足每个瓶子都恰好装两个值相同的变量的概率。
请输出概率乘上 \(D^n\) 后对 \(998244353\) 取模的值。
数据范围
测试点编号 | \(D\) | \(n\) | \(m\) |
---|---|---|---|
\(25\) | \(\le 100000\) | \(\le 1000000000\) | \(\le n\) |
所有测试点均满足 \(0\le m\le 10^9,1\le n\le 10^9,1\le D\le 10^5\)。
解题思路
显然如果 \(m * 2 + D \le n\) 的话概率就是 1
还是要推推式子
设 \(f_i\) 表示至少有 i 个奇数的方案数,有
\[\large f_i = n!{D \choose i} \left[x^n\right]\left(\frac {e^x-e^{-x}}{2} \right)^{i}e^{(D-i)x}\\ \large = n!{D \choose i}\frac 1{2^i} \sum_{j=0}^i\left[x^n\right](-1)^{i-j}{i\choose j}e^{jx}e^{-(i-j)x}e^{(D-i)x}\\ \large = n!{D \choose i}\frac 1{2^i} \sum_{j=0}^i\left[x^n\right](-1)^{i-j}{i\choose j}e^{(D-2(i-j))x}\\ \large = n!{D \choose i}\frac 1{2^i} \sum_{j=0}^i(-1)^{i-j}{i\choose j}\frac {(D-2(i-j))^n}{n!}\\ \large = n!D!\frac 1{2^i(D-i)!} \sum_{j=0}^i\frac{1}{j!(i-j)!}(-1)^{i-j}\frac {(D-2(i-j))^n}{n!}\\ \large = \frac {D!}{2^i(D-i)!} \sum_{j=0}^i\frac{1}{j!}\frac{(-1)^{i-j}(D-2(i-j))^n}{(i-j)!}\\ \]
最后二项式反演一下即可,要用多项式加速
#include
#include
#include
#include
#include
#include
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template
inline void write(F x, char ed = '\n')
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template
inline void Mx(T &x, T y) { x < y && (x = y); }
template
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 300500;
const int P = 998244353;
ll A[N], B[N], E[N], inv[N], jie[N];
void Print(ll *A, int len) {
for (int i = 0;i < len; i++)
write(A[i], ' ');
puts("");
}
ll fpw(ll x, ll mi) {
ll res = 1;
for (; mi; mi >>= 1, x = x * x % P)
if (mi & 1) res = res * x % P;
return res;
}
ll D, n, m;
int r[N], lim = 1;
void init(void) {
while (lim <= 2 * D) lim <<= 1;
int len = lim >> 1;
for (int i = 0;i < lim; i++)
r[i] = (r[i>>1]>>1) | ((i & 1) ? len : 0);
E[1] = 1;
for (int i = 2;i < lim; i <<= 1) {
ll *e0 = E + i / 2, *e1 = E + i;
ll w = fpw(3, (P - 1) / (i << 1));
for (int j = 0;j < i; j += 2)
e1[j] = e0[j>>1], e1[j+1] = e1[j] * w % P;
}
jie[0] = jie[1] = inv[0] = inv[1] = 1;
for (int i = 2;i < lim; i++) {
jie[i] = jie[i-1] * i % P;
inv[i] = inv[P % i] * (P - P / i) % P;
}
for (int i = 2;i < lim; i++)
inv[i] = inv[i-1] * inv[i] % P;
}
void dft(ll *A) {
for (int i = 1;i < lim; i++)
if (r[i] > i) swap(A[i], A[r[i]]);
for (int i = 1;i < lim; i <<= 1) {
for (int j = 0;j < lim; j += (i << 1)) {
ll *f = A + j, *g = f + i; ll *e = E + i;
for (int k = 0;k < i; k++) {
int x = f[k], y = e[k] * g[k] % P;
f[k] = (x + y) % P, g[k] = (x + P - y) % P;
}
}
}
}
void idft(ll *A) {
dft(A); ll inv = fpw(lim, P - 2);
reverse(A + 1, A + lim);
for (int i = 0;i < lim; i++)
A[i] = A[i] * inv % P;
}
int main() {
read(D), read(n), read(m), init();
if (m * 2 > n) return puts("0"), 0;
if (m * 2 <= n - D) return write(fpw(D, n)), 0;
for (int i = 0;i <= D; i++) {
A[i] = inv[i], B[i] = fpw((D + P - 2 * i) % P, n) * inv[i] % P;
if (i & 1) B[i] = (P - B[i]) % P;
}
dft(A), dft(B);
// Print(A, lim), Print(B, lim);
for (int i = 0;i < lim; i++) A[i] = A[i] * B[i] % P;
idft(A);
for (int i = 0;i <= D; i++)
A[i] = A[i] * jie[D] % P * fpw(2, P - 1 - i) % P * inv[D-i] % P * jie[i] % P;
for (int i = 0;i <= D; i++) B[D-i] = i & 1 ? P - inv[i] : inv[i];
for (int i = D + 1;i < lim; i++) A[i] = B[i] = 0;
// Print(A, D), Print(B, D);
dft(A), dft(B);
for (int i = 0;i < lim; i++) A[i] = A[i] * B[i] % P;
idft(A);
ll ans = 0;
for (int i = 0;i <= n - m - m; i++) ans = (ans + A[D + i] * inv[i]) % P;
write(ans);
return 0;
}