Loading

CF755G PolandBall and Many Other Balls

CF755G PolandBall and Many Other Balls

解法一

枚举选出的 \(k\) 组里有多少个 \(2\) 个球组成的,那么取 \(i\) 组的答案 \(ans_i\) 就是 \(\sum_{j=0}^{i}\binom{n-j}{j}\binom{n-2j}{i-j}\) ,就是先选好 \(2\) 个球的位置,剩下的随便选即可。

我小学奥数没学好,选好 \(2\) 个球的位置想了好一会。。。就是考虑拿出 \(k\) 个球,插到前面 \(n-k\) 个缝隙里去,插入到一个缝隙代表选它和它前面的组成一组,所以 \(1\) 前面的空隙不能算,共 \(n-k\) 个空隙。

这个式子挺不好算的,那个 \(2\) 看着就很烦,然而去不掉,而且拆了半天式子没法卷积。

那就暴力拆阶乘吧!!!

\[ans_i=\sum_{j=0}^{i}\binom{n-j}{j}\binom{n-2j}{i-j}\\ =\sum_{j=0}^{i}\dfrac{(n-j)!}{j!(n-2j)}\dfrac{(n-2j)!}{(i-j)!(n-i-j)!}\\ =\sum_{j=0}^{i}\dfrac{(n-j)!}{j!(n-2j)!(i-j)!}\\ =\sum_{j=0}^{i}\dfrac{i!}{j!(i-j)!}\dfrac{(n-j)!}{(n-i-j)!i!}\\ =\sum_{j=0}^{i}\binom{i}{j}\binom{n-j}{i} \]

推到这里卡了两个小时,怎么都不会卷。。。然后点开了题解。。。

草,组合意义yyds!(这是我开坑多项式之后的第二道题,就能深切感受到组合意义的强大还是很幸运的吧)

这个式子的组合以意义就是:给你 \(n\) 个球排成一排,\([1,i]\) 之间先选 \(j\) 个球,再从 \(n\) 个球中任选 \(i\) 个球,球不能选重的方案数。

根据容斥原理,恰好 \(0\) 个球重复等于:至少 \(0\) 个球重复-至少 \(1\) 个球重复+至少 \(2\) 个球重复……

怎么算至少:前 \(i\) 个位置钦定重复 \(j\) 个,剩下还要选 \(i-j\) 个不重复的,有 \(n-i\) 个位置,每一个位置还要决定选不选。

\[\sum_{j=0}^{i}\binom{i}{j}\binom{n-j}{i}=\sum_{j=0}^{i}(-1)^j\binom{i}{j}\binom{n-j}{i-j}2^{i-j} \]

然后随便推推就能卷了。

\[\sum_{j=0}^{i}(-1)^j\binom{i}{j}\binom{n-j}{i-j}2^{i-j}\\ =\sum_{j=0}^{i}(-1)^{j}\dfrac{i!}{j!(i-j)!}\dfrac{(n-j)!}{(i-j)!(n-i)!}2^{i-j}\\ =i!n^{\underline i}\sum_{j=0}^{i}\dfrac{(-1)^{j}}{j!n^{\underline j}}\times \dfrac{2^{i-j}}{(i-j)!(i-j)!} \]

注意推的时候要关注 \(n\) 的大小,不要还留个 \(n!\) 在里面。

其实下降幂也没啥好怕的,就是一个符号而已,又不是下降幂多项式,不要像我之前一样看到下降幂就逃。

\(n^{\underline x}=n(n-1)(n-2)\cdots(n-x)\)

const int N=1<<15;
const int M=N<<2;
#define mod 998244353
int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
int lg,lim,rev[M];
void init(const int&n){
	for(lg=0,lim=1;lim<n;lim<<=1,++lg);
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int*a,int op){
	for(int i=0;i<lim;++i)
		if(i>rev[i])swap(a[i],a[rev[i]]);
	int g=op?3:qpow(3,mod-2);
	for(int i=1;i<lim;i<<=1){
		int wn=qpow(g,(mod-1)/(i<<1));
		for(int j=0;j<lim;j+=i<<1){
			int w0=1;
			for(int k=0;k<i;++k,w0=1ll*w0*wn%mod){
				const int X=a[j+k],Y=1ll*w0*a[i+j+k]%mod;
				a[j+k]=(X+Y)%mod,a[i+j+k]=(X-Y+mod)%mod;
			}
		}
	}
	if(op)return;int ilim=qpow(lim,mod-2);
	for(int i=0;i<lim;++i)a[i]=1ll*a[i]*ilim%mod;
}

int n,k,dwn[N],pw2[N],f[M],g[M],fac[N];
void initmath(const int&C){
	dwn[0]=1;rep(i,1,C)dwn[i]=1ll*dwn[i-1]*(n-i+1)%mod;
	pw2[0]=1;rep(i,1,C)pw2[i]=(pw2[i-1]<<1)%mod;
	fac[0]=1;rep(i,1,C)fac[i]=1ll*fac[i-1]*i%mod;
}
signed main(){
	n=read(),k=read(),initmath(k);
	for(int i=0,j=1;i<=k;++i,j^=1)f[i]=j?qpow(1ll*fac[i]*dwn[i]%mod,mod-2):mod-qpow(1ll*fac[i]*dwn[i]%mod,mod-2);
	for(int i=0;i<=k;++i)g[i]=1ll*pw2[i]*qpow(1ll*fac[i]*fac[i]%mod,mod-2)%mod;
	init(k<<1),NTT(f,1),NTT(g,1);
	for(int i=0;i<lim;++i)f[i]=1ll*f[i]*g[i]%mod;
	NTT(f,0);
	for(int i=1;i<=k;++i)f[i]=1ll*f[i]*fac[i]%mod*dwn[i]%mod;
	for(int i=1;i<=k;++i)printf("%d ",f[i]);
	return 0;
}

解法二

倍增FFT,跟着 Karry5307的题解学的。

倍增FFT要求我们能从 \(F_n\) 转移到 \(F_{n+1}\)\(F_{2n}\) ,其实和快速幂很像,然后 \(n=10^9\) 级别的只需要 \(O(\log n)\) 次FFT就够了。

关于这道题,我们设 \(dp_{n,k}\) 表示 \(n\) 个球分 \(k\) 组的方案数,\(F_n=\sum_{k=0}dp_{n,k}\)

显然有 \(dp_{n,k}=dp_{n-1,k-1}+dp_{n-2,k-1}+dp_{n-1,k}\)

写成生成函数形式就是 \(F_{n}=xF_{n-1}+xF_{n-2}+F_{n-1}\)

这样子我们已经可以从 \(n\) 转移到 \(n+1\) 了。

然后换一种转移,不能递推了。

假设我们把 \(a\) 个球和 \(b\) 个球拼起来。

可以直接拼,也可以让 \(a\) 个球的最右边与 \(b\) 个球的最左边拼起来

\(dp_{a+b,k}=\sum_{i=0}^{k}dp_{a,i}dp_{b,k-i}+\sum_{i=1}^{k}dp_{a-1,i-1}dp_{b-1,k-i}\)

写成生成函数形式就是

\[\begin{cases} F_{2n}(x)=F_{n}(x)^2+xF_{n-1}(x)^2\\ F_{2n-1}(x)=F_{n-1}(x)F_{n}(x)+xF_{n-2}(x)F_{n-1}(x)\\ F_{2n-2}(x)=F_{n-1}(x)^2+xF_{n-2}(x)^2 \end{cases} \]

然后就可以从 \(n\) 推到 \(2n\) 了。

显然我们需要同时维护 \(F_{n-2},F_{n-1},F_{n}\) 的值,常数可不小啊。

const int N=1<<15;
const int M=N<<2;
int n,k,f[3][M];
namespace poly{
#define mod 998244353
int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
void fmod(int&x){x-=mod,x+=x>>31&mod;}
int rev[M],lg,lim;
void init_poly(const int&n){
	for(lg=0,lim=1;lim<=n;lim<<=1,++lg);
	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int*a,int op){
	for(int i=0;i<lim;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
	int g=op?3:qpow(3,mod-2);
	for(int i=1;i<lim;i<<=1){
		int wn=qpow(g,(mod-1)/(i<<1));
		for(int j=0;j<lim;j+=i<<1){
			int w0=1;
			for(int k=0;k<i;++k,w0=1ll*w0*wn%mod){
				const int X=a[j+k],Y=1ll*w0*a[i+j+k]%mod;
				a[j+k]=(X+Y)%mod,a[i+j+k]=(X-Y+mod)%mod;
			}
		}
	}
	if(op)return;int ilim=qpow(lim,mod-2);
	for(int i=0;i<lim;++i)a[i]=1ll*ilim*a[i]%mod;
}
#define clr(a,n) memset(a,0,sizeof(int)*(n))
#define cpy(a,b,n) memcpy(a,b,sizeof(int)*(n))
void mul(const int&n){
	static int A[M],B[M],C[M],D[M],E[M];
	NTT(f[0],1),NTT(f[1],1),NTT(f[2],1);
	for(int i=0;i<lim;++i)
		A[i]=1ll*f[0][i]*f[0][i]%mod,
		B[i]=1ll*f[1][i]*f[1][i]%mod,
		C[i]=1ll*f[0][i]*f[1][i]%mod,
		D[i]=1ll*f[2][i]*f[1][i]%mod,
		E[i]=1ll*f[2][i]*f[2][i]%mod;
	NTT(A,0),NTT(B,0),NTT(C,0),NTT(D,0),NTT(E,0);
	clr(f[0],lim),clr(f[1],lim),clr(f[2],lim);
	for(int i=0;i<=n;++i)
		fmod(f[0][i]=A[i]+(i?B[i-1]:0)),
		fmod(f[1][i]=C[i]+(i?D[i-1]:0)),
		fmod(f[2][i]=B[i]+(i?E[i-1]:0));
}
void add(const int&n){
	cpy(f[2],f[1],n+1),cpy(f[1],f[0],n+1),f[0][0]=1;
	for(int i=1;i<=n;++i)f[0][i]=((f[1][i]+f[1][i-1])%mod+f[2][i-1])%mod;
}

}


signed main(){
	n=read(),k=read();
	f[1][0]=1;
	f[0][0]=f[0][1]=1;
	poly::init_poly(k<<1);
	for(int i=log2(n)-1;i>=0;--i){
		poly::mul(k);
		if(n>>i&1)poly::add(k);
	}
	for(int i=1;i<=k;++i)printf("%d ",f[0][i]);puts("");
	return 0;
}
posted @ 2020-12-26 08:01  zzctommy  阅读(111)  评论(0编辑  收藏  举报