洛谷P5401 [CTS2019]珍珠(生成函数)

洛谷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;
}

你可能感兴趣的:(洛谷P5401 [CTS2019]珍珠(生成函数))