[CTS2019]珍珠 解题报告

\(n\) 个珍珠,每个珍珠会等概率拥有一个在 \([1,D]\) 内的颜色,希望得到的珍珠可以装进 \(m\) 个瓶子里,使得每个瓶子里都有两个颜色一样的珍珠,求愿望实现的概率,答案乘上 \(D^n\) 再对 \(998244353\) 取模。

\(n,m\le 10^9 , D\le 10^5\)

答案乘上 \(D^n\) 就是满足条件的个数,所以这是一道计数题。

第一步先将题目给出的条件转化为数学语言,设 \(gs_x\) 为颜色为 \(x\) 的珍珠的个数,那么有

\[\begin{cases}\sum\limits_{i=1}^D gs_i = n\\\sum\limits_{i=1}^D \left\lfloor\frac{gs_i}{2}\right\rfloor \ge m\end{cases} \]

整合一下就有 \(\sum\limits_{i=1}^D gs_i\%2 \le n-2m\) ,如果像我一样想着直接做,那么就要处理一个将 \(n\) 个数分成 \(D\) 个子集,要求每个子集的大小是偶数这样一个东西,然后就会发现很难去重,如果用生成函数就要维护两个和式。

这时就要套路的运用二项式反演将恰好 \(k\) 个转为钦定 \(k\) 个,其余随便,设 \(g_x\) 为恰好有 \(x\) 个奇数的方案数, \(f_x\) 为钦定 \(x\) 个为奇数,其余随便的方案数,那么有 \(f_x=\sum\limits_{i=x}^D \binom{i}{x} g_i\) ,根据二项式反演有 \(g_x=\sum\limits_{i=x}^D (-1)^{i-x} \binom{i}{x} f_i\) ,最后答案是 \(\sum\limits_{i=0}^{n-2m} g_i\)

可以使用生成函数计算 \(f_x\) 的值,首先有大小为奇数的 \(EGF\{[0,1,0,1,...]\}=\sum\limits_{i=0}^{\infty} \frac{1^i-(-1)^ix^i}{2i!}=\frac{e^x-e^{-x}}{2}\) ,那么有

\[\begin{aligned} f_k & =\binom{D}{k}n![x^n](\frac{e_x-e^{-x}}{2})^k (e^x)^{D-k} \\ & =\binom{D}{k}\frac{n!}{2^k}[x^n]\sum\limits_{i=0}^k \binom{k}{i} e^{ix} (-e)^{-(k-i)x} e^{(D-k)x} \\ & =\binom{D}{k}\frac{n!}{2^k}\sum\limits_{i=0}^k \binom{k}{i} (-1)^{i} [x^n]e^{(D-2i)x} \\ & =\binom{D}{k}\frac{n!}{2^k}\sum\limits_{i=0}^k \binom{k}{i} (-1)^{i} \frac{(D-2i)^n}{n!} \\ & =\frac{D!}{(D-k)!2^k}\sum\limits_{i=0}^k \frac{(-1)^i (D-2i)^n}{i!(k-i)!} \end{aligned} \]

显然是卷积的形式,可以用 \(NTT\) 计算 \(f_x\) ,然后将二项式反演的式子拆开就是一个差值卷积了。

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int M=4e5+5,JYY=998244353;

int read(){
    int x=0,y=1;char ch=getchar();
    while(ch<'0'||ch>'9') y=(ch=='-'?-1:1),ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*y;
}

int rev[M],wn[35],fac[M],ifac[M];
int qpow(int x,int y){
	int res=1;x%=JYY,y%=(JYY-1);
	for(;y;x=x*x%JYY,y>>=1) if(y&1) res=res*x%JYY;
	return res;
}
void getwn(){
	for(int i=0;i<=25;i++){
		int t=(1<<i);
		wn[i]=qpow(3,(JYY-1)/t);
	}
}

void reverse(int A[],int l,int r){ for(int i=0;i<=(r-l)/2;i++) swap(A[l+i],A[r-i]); }
void NTT(int f[],int L,int inv){
	for(int i=0;i<L;i++) if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int mid=1,id=1;mid<L;mid<<=1,id++)
		for(int R=mid<<1,j=0;j<L;j+=R)
			for(int k=0,w=1;k<mid;k++,w=w*wn[id]%JYY){
				int u=f[j+k],v=w*f[j+k+mid]%JYY;
				f[j+k]=(u+v)%JYY;
				f[j+k+mid]=(u-v+JYY)%JYY;
			}
	if(inv==-1){
		reverse(f,1,L-1);int nL=qpow(L,JYY-2);
		for(int i=0;i<L;i++) f[i]=f[i]*nL%JYY;
	}
}
void poly_mul(int A[],int B[],int C[],int L1,int L2){
	int len=1,L=0;static int TA[M],TB[M];
	for(;len<L1+L2;len<<=1) L++;
	for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
	for(int i=0;i<len;i++) TA[i]=(i<L1?A[i]:0);
	for(int i=0;i<len;i++) TB[i]=(i<L2?B[i]:0);
	NTT(TA,len,1),NTT(TB,len,1);
	for(int i=0;i<len;i++) C[i]=TA[i]*TB[i]%JYY;
	NTT(C,len,-1);
	for(int i=L1+L2-1;i<len;i++) C[i]=0;
}

int D,n,m,Ans=0,a[M],b[M],f[M];
void solve(){
    D=read(),n=read(),m=read();
    if(m+m<=n-D) return (void)(printf("%lld\n",qpow(D,n)));
    if(m+m>n) return (void)(printf("0\n"));
    fac[0]=fac[1]=ifac[0]=ifac[1]=1;getwn();
    for(int i=2;i<=D;i++) ifac[i]=(JYY-JYY/i)*ifac[JYY%i]%JYY;
    for(int i=2;i<=D;i++) fac[i]=fac[i-1]*i%JYY,ifac[i]=ifac[i-1]*ifac[i]%JYY;
    for(int i=0;i<=D;i++) a[i]=qpow(JYY-1,i)*qpow(D-i-i+JYY,n)%JYY*ifac[i]%JYY,b[i]=ifac[i];
    poly_mul(a,b,f,D+1,D+1);
    for(int i=0;i<=D;i++) f[i]=f[i]*fac[D]%JYY*qpow((JYY+1)/2,i)%JYY*ifac[D-i]%JYY;
    for(int i=0;i<=D;i++) a[i]=f[i]*fac[i]%JYY,b[D-i]=qpow(JYY-1,i)*ifac[i]%JYY;
    poly_mul(a,b,f,D+1,D+1);
    for(int i=0;i<=n-m-m;i++) Ans=(Ans+f[D+i]*ifac[i]%JYY)%JYY;
    printf("%lld\n",Ans);
}

signed main(){
	// freopen("shuju.in","r",stdin);
    solve();
}
posted @ 2021-01-21 20:26  Dabuliuzp  阅读(152)  评论(0编辑  收藏  举报
/* */ 返回顶端