洛谷 P5401 - [CTS2019]珍珠(NTT+二项式反演)

题面传送门

一道多项式的 hot tea

首先考虑将题目的限制翻译成人话,我们记 \(c_i\)\(i\) 的出现次数,那么题目的限制等价于 \(\sum\limits_{i=1}^D\lfloor\dfrac{c_i}{2}\rfloor\le m\)。不难发现这里涉及下取整,稍微有些棘手,因此考虑将这个下取整去掉,显然 \(\lfloor\dfrac{c_i}{2}\rfloor=\dfrac{c_i-c_i\bmod 2}{2}\),故原式可化为 \(\sum\limits_{i=1}^D\dfrac{c_i-c_i\bmod 2}{2}\le m\),我们将左右两边同乘 \(2\),再稍微变个形可得 \(\sum\limits_{i=1}^Dc_i-\sum\limits_{i=1}^Dc_i\bmod 2\le 2m\),显然 \(\sum\limits_{i=1}^Dc_i=n\),故 \(\sum\limits_{i=1}^Dc_i\bmod 2\le n-2m\),也就是 \(c\) 数组中奇数个数 \(\le n-2m\)

方便起见我们先特判掉 \(n-2m<0\)\(n-2m\ge D\) 的情况,两种情况的答案分别为 \(0\)\(D^n\)。接下来我们着重考虑 \(0\le n-2m\lt D\) 的情况。我们记 \(f_i\)\(c\) 数列中恰好存在 \(i\) 个奇数的方案数,那么答案即为 \(\sum\limits_{i=0}^{n-2m}f_i\),注意到这个“恰好 \(i\) 个”很棘手,因此按照套路设 \(g_i\) 表示钦定 \(i\)\(c_j\) 为奇数,剩余随便排的方案数,求出 \(g_i\) 后即可二项式反演求出 \(f_i\),即 \(f_i=\sum\limits_{j=i}\dbinom{j}{i}(-1)^{j-i}g_j\),把式子稍微转化一下即可得到 \(f_i=\dfrac{1}{i!}\sum\limits_{j}j!g_j\times(-1)^{j-i}\dfrac{1}{(j-i)!}\),这显然是一个差卷积的形式,因此求出 \(g_i\) 之后一遍差卷积即可求出答案了。

接下来我们的任务就是求出 \(g_i\)。首先我们肯定要从 \(D\) 种数中选出 \(i\) 个并钦定它们出现次数为奇数,这样选的方案数为 \(\dbinom{D}{i}\)。其次,注意到求出每个数的出现次数后求原序列的方案数是一个二项加法卷积,因此考虑 EGF,根据生成函数那一套理论,我们钦定出现次数为奇数的 EGF 为 \(\dfrac{e^x-e^{-x}}{2}\),其余没有限制的数的 EGF 为 \(e^x\),故 \(g_i=\dbinom{D}{i}n![x^n](\dfrac{e^x-e^{-x}}{2})^i(e^x)^{D-i}\),我们考虑用二项式定理展开并将其变个形,则可以得到:

\[\begin{aligned} g_i&=\dbinom{D}{i}n![x^n](\dfrac{e^x-e^{-x}}{2})^i(e^x)^{D-i}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}[x^n](e^x-e^{-x})^i(e^x)^{D-i}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}[x^n]\sum\limits_{j=0}^i\dbinom{i}{j}(e^x)^j(-e^{-x})^{i-j}(e^x)^{D-i}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}[x^n]\sum\limits_{j=0}^i\dbinom{i}{j}(e^x)^j(e^{-x})^{i-j}(e^x)^{D-i}(-1)^{i-j}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}[x^n]\sum\limits_{j=0}^i\dbinom{i}{j}(e^x)^{D-2(i-j)}(-1)^{i-j}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}\sum\limits_{j=0}^i\dbinom{i}{j}[x^n](e^x)^{D-2(i-j)}(-1)^{i-j}\\ &=\dbinom{D}{i}\dfrac{n!}{2^i}\sum\limits_{j=0}^i\dbinom{i}{j}\dfrac{1}{n!}(D-2(i-j))^n(-1)^{i-j}\\ &=\dbinom{D}{i}\dfrac{1}{2^i}\sum\limits_{j=0}^i\dfrac{i!}{j!(i-j)!}(D-2(i-j))^n(-1)^{i-j}\\ &=\dbinom{D}{i}\dfrac{1}{2^i}i!\sum\limits_{j=0}^i\dfrac{1}{j!}·\dfrac{(D-2(i-j))^n(-1)^{i-j}}{(i-j)!} \end{aligned} \]

推到这一步不难发现这是一个卷积的形式,记 \(a_j=\dfrac{1}{j!},b_j=\dfrac{(D-2j)^n(-1)^{j}}{j!}\),跑遍卷积即可求出 \(g_i\)

时间复杂度 \(D\log D\)

const int MAXP=1<<18;
const int MOD=998244353;
const int pr=3;
const int ipr=(MOD+1)/3;
const int INV2=MOD+1>>1;
int qpow(int x,int e){
	int ret=1;
	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
	return ret;
}
int D,n,m,fac[MAXP+5],ifac[MAXP+5];
void init_fac(int n){
	for(int i=(fac[0]=ifac[0]=ifac[1]=1)+1;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
}
int binom(int n,int k){return 1ll*fac[n]*ifac[k]%MOD*ifac[n-k]%MOD;}
int rev[MAXP+5];
void NTT(vector<int> &a,int len,int type){
	int lg=31-__builtin_clz(len);
	for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
	for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=2;i<=len;i<<=1){
		int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
		for(int j=0;j<len;j+=i){
			for(int k=0,w=1;k<(i>>1);k++,w=1ll*w*W%MOD){
				int X=a[j+k],Y=1ll*a[(i>>1)+j+k]*w%MOD;
				a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
			}
		}
	}
	if(type==-1){
		int ivn=qpow(len,MOD-2);
		for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
	}
}
vector<int> conv(vector<int> a,vector<int> b){
	int LEN=1;while(LEN<a.size()+b.size()) LEN<<=1;
	a.resize(LEN,0);b.resize(LEN,0);NTT(a,LEN,1);NTT(b,LEN,1);
	for(int i=0;i<LEN;i++) a[i]=1ll*a[i]*b[i]%MOD;
	NTT(a,LEN,-1);return a;
}
int main(){
	scanf("%d%d%d",&D,&n,&m);init_fac(D);
	if(n-2*m>=D) return printf("%d\n",qpow(D,n)),0;
	if(n-2*m<0) return printf("0\n"),0;
	vector<int> a(D+1),b(D+1);
	for(int i=0;i<=D;i++){
		a[i]=ifac[i];
		if(i&1) b[i]=(MOD-1ll*qpow((D-2*i+MOD)%MOD,n)*ifac[i]%MOD)%MOD;
		else b[i]=1ll*qpow((D-2*i+MOD)%MOD,n)*ifac[i]%MOD;
	}
	vector<int> f=conv(a,b),h(D+1);f.resize(D+1);
	for(int i=0,pw=1;i<=D;i++,pw=1ll*pw*INV2%MOD)
		f[i]=1ll*f[i]*binom(D,i)%MOD*pw%MOD*fac[i]%MOD;
	for(int i=0;i<=D;i++) f[i]=1ll*f[i]*fac[i]%MOD;
	for(int i=0;i<=D;i++){
		if(i&1) h[D-i]=MOD-ifac[i];
		else h[D-i]=ifac[i];
	} vector<int> g=conv(f,h);int ans=0;
	for(int i=0;i<=n-2*m;i++) ans=(ans+1ll*g[D+i]*ifac[i])%MOD;
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-05-05 10:08  tzc_wk  阅读(73)  评论(0)    收藏  举报