CTS 2019 珍珠 题解

题目传送门

题目大意: n n n 颗珍珠,现在给他们随机染色,颜色区间为 [ 1 , D ] [1,D] [1,D],两个颜色相同的珍珠可以装一瓶,现在问有多少种情况可以装至少 m m m 瓶?

题解

有一个比较显然的结论:假如有 k k k 个颜色出现奇数次,那么能装的瓶数就是 ⌊ n − k 2 ⌋ \left\lfloor \dfrac {n-k} 2 \right\rfloor 2nk

由于要求 ⌊ n − k 2 ⌋ ≥ m \left\lfloor \dfrac {n-k} 2 \right\rfloor \geq m 2nkm,所以 n − k ≥ 2 m n-k\geq 2m nk2m,即 k ≤ n − 2 m k\leq n-2m kn2m

显然可以特判两种情况: 2 m > n 2m>n 2m>n 时答案为 0 0 0 D ≤ n − 2 m D\leq n-2m Dn2m 时答案为 D n D^n Dn

f i f_i fi 表示至少有 i i i 种颜色出现了奇数次, g i g_i gi 表示恰好有 i i i 种颜色出现了奇数次,那么有:
f i = ∑ j = i D C j i g j f_i=\sum_{j=i}^D C_j^i g_j fi=j=iDCjigj

二项式反演一下得到:
g i = ∑ j = i D ( − 1 ) j − i C j i f j = 1 i ! ∑ j = i D ( − 1 ) j − i ( j − i ) ! × f j j ! \begin{aligned} g_i&=\sum_{j=i}^D (-1)^{j-i} C_j^i f_j\\ &=\frac 1 {i!}\sum_{j=i}^D \frac {(-1)^{j-i}} {(j-i)!} \times f_j j! \end{aligned} gi=j=iD(1)jiCjifj=i!1j=iD(ji)!(1)ji×fjj!

这是个卷积的形式,将 ( − 1 ) j − i ( j − i ) ! \dfrac {(-1)^{j-i}} {(j-i)!} (ji)!(1)ji 翻转一下变成 ( − 1 ) D − j + i ( D − j + i ) ! \dfrac {(-1)^{D-j+i}} {(D-j+i)!} (Dj+i)!(1)Dj+i,和后面的卷在一起得到第 D + i D+i D+i 位的值,然后将卷出来的数组左移 D D D 位得到 g i g_i gi

所以求出 f f f 就能求出 g g g 了。那么下面的问题就是怎么求 f f f

考虑对每个颜色构造 E G F EGF EGF,对于 f k f_k fk 而言,至少有 k k k 个颜色出现奇数次,这些颜色的生成函数系数为 0 , 1 , 0 , 1 , . . . 0,1,0,1,... 0,1,0,1,...,即 e x − e − x 2 \frac {e^x-e^{-x}} 2 2exex,剩下的 n − k n-k nk 种颜色随便取多少个,即 e x e^x ex,那么有:
f k = C D k n ! [ x n ] ( e x − e − x 2 ) k e x ( D − k ) = n ! 2 k C D k [ x n ] ∑ j = 0 k C k j e j x ( − e − x ) ( k − j ) e x ( D − k ) = n ! 2 k C D k ∑ j = 0 k C k j ( − 1 ) k − j [ x n ] e ( D + 2 ( j − k ) ) x = n ! 2 k C D k ∑ j = 0 k C k j ( − 1 ) k − j ( D + 2 j − 2 k ) n n ! = n ! D ! k ! ( D − k ) ! 2 k ∑ j = 0 k k ! j ! ( k − j ) ! ( − 1 ) k − j ( D + 2 j − 2 k ) n n ! = D ! ( D − k ) ! 2 k ∑ j = 0 k ( D − 2 ( k − j ) ) n ( − 1 ) k − j ( k − j ) ! × 1 j ! \begin{aligned} f_k&=C_D^k n![x^n]\left(\frac {e^x-e^{-x}} 2\right)^k e^{x(D-k)}\\ &=\frac {n!} {2^k} C_D^k [x^n] \sum_{j=0}^k C_k^j e^{jx} (-e^{-x})^{(k-j)} e^{x(D-k)}\\ &=\frac {n!} {2^k} C_D^k \sum_{j=0}^k C_k^j (-1)^{k-j} [x^n]e^{(D+2(j-k))x}\\ &=\frac {n!} {2^k} C_D^k \sum_{j=0}^k C_k^j (-1)^{k-j} \frac {(D+2j-2k)^n} {n!}\\ &=\frac {n!D!} {k!(D-k)!2^k} \sum_{j=0}^k \frac {k!} {j!(k-j)!}(-1)^{k-j} \frac {(D+2j-2k)^n} {n!}\\ &=\frac {D!} {(D-k)!2^k} \sum_{j=0}^k \frac {(D-2(k-j))^n(-1)^{k-j}} {(k-j)!}\times \frac {1} {j!}\\ \end{aligned} fk=CDkn![xn](2exex)kex(Dk)=2kn!CDk[xn]j=0kCkjejx(ex)(kj)ex(Dk)=2kn!CDkj=0kCkj(1)kj[xn]e(D+2(jk))x=2kn!CDkj=0kCkj(1)kjn!(D+2j2k)n=k!(Dk)!2kn!D!j=0kj!(kj)!k!(1)kjn!(D+2j2k)n=(Dk)!2kD!j=0k(kj)!(D2(kj))n(1)kj×j!1

发现后面又是个卷积的形式,卷一下答案就出来了。

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 300010
#define mod 998244353
#define bin(x) (1<<(x))

int D,n,m;
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
#define inv(x) ksm(x,mod-2)
int limit=1,l=0,r[maxn];
struct NTT{
	int *w[30];NTT(){
		for(int i=1,wn;i<=18;i++){
			w[i]=new int[bin(i)];w[i][0]=1;wn=ksm(3,(mod-1)/bin(i));
			for(int j=1;j<bin(i-1);j++)w[i][j]=1ll*w[i][j-1]*wn%mod;
		}
	}
	void dft(int *f,int type=0)
	{
		if(type)reverse(f+1,f+limit);
		for(int i=1;i<limit;i++)if(i<r[i])swap(f[i],f[r[i]]);
		for(int mid=1,Lg=1;mid<limit;mid<<=1,Lg++)for(int j=0;j<limit;j+=(mid<<1))for(int i=0;i<mid;i++)
		{int t=1ll*f[j+i+mid]*w[Lg][i]%mod;f[j+i+mid]=(f[j+i]-t+mod)%mod;f[j+i]=(f[j+i]+t)%mod;}
	}
}ntt;
void FFT(int *f,int *g){
	ntt.dft(f);ntt.dft(g);for(int i=0;i<limit;i++)f[i]=1ll*f[i]*g[i]%mod;
	ntt.dft(f,1);for(int i=0,inv_l=inv(limit);i<limit;i++)f[i]=1ll*f[i]*inv_l%mod;
}
int fac[maxn],inv_fac[maxn],F[maxn],G[maxn];

int main()
{
	scanf("%d %d %d",&D,&n,&m);
	if(D<=n-2*m)return printf("%d",ksm(D,n)),0;
	if(n<2*m)return printf("0"),0;
	while(limit<=2*D+1)limit<<=1,l++;for(int i=0;i<limit;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	fac[0]=inv_fac[0]=1;for(int i=1;i<=maxn-10;i++)fac[i]=1ll*fac[i-1]*i%mod;
	inv_fac[maxn-10]=inv(fac[maxn-10]);for(int i=maxn-11;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
	
	for(int i=0;i<=D;i++)F[i]=1ll*ksm((D-2*i+mod)%mod,n)*(i&1?mod-inv_fac[i]:inv_fac[i])%mod,G[i]=inv_fac[i];FFT(F,G);
	for(int i=0;i<=D;i++)F[i]=1ll*F[i]*fac[D]%mod*inv_fac[D-i]%mod*inv(ksm(2,i))%mod*fac[i]%mod;
	for(int i=0;i<=D;i++)G[D-i]=(i&1?mod-inv_fac[i]:inv_fac[i]);for(int i=D+1;i<limit;i++)F[i]=G[i]=0;FFT(F,G);
	int ans=0;for(int i=0;i<=n-2*m;i++)ans=(ans+1ll*F[i+D]*inv_fac[i]%mod)%mod;printf("%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值