CF755G PolandBall and Many Other Balls
解法一
枚举选出的 (k) 组里有多少个 (2) 个球组成的,那么取 (i) 组的答案 (ans_i) 就是 (sum_{j=0}^{i}inom{n-j}{j}inom{n-2j}{i-j}) ,就是先选好 (2) 个球的位置,剩下的随便选即可。
我小学奥数没学好,选好 (2) 个球的位置想了好一会。。。就是考虑拿出 (k) 个球,插到前面 (n-k) 个缝隙里去,插入到一个缝隙代表选它和它前面的组成一组,所以 (1) 前面的空隙不能算,共 (n-k) 个空隙。
这个式子挺不好算的,那个 (2) 看着就很烦,然而去不掉,而且拆了半天式子没法卷积。
那就暴力拆阶乘吧!!!
推到这里卡了两个小时,怎么都不会卷。。。然后点开了题解。。。
草,组合意义yyds!(这是我开坑多项式之后的第二道题,就能深切感受到组合意义的强大还是很幸运的吧)
这个式子的组合以意义就是:给你 (n) 个球排成一排,([1,i]) 之间先选 (j) 个球,再从 (n) 个球中任选 (i) 个球,球不能选重的方案数。
根据容斥原理,恰好 (0) 个球重复等于:至少 (0) 个球重复-至少 (1) 个球重复+至少 (2) 个球重复……
怎么算至少:前 (i) 个位置钦定重复 (j) 个,剩下还要选 (i-j) 个不重复的,有 (n-i) 个位置,每一个位置还要决定选不选。
然后随便推推就能卷了。
注意推的时候要关注 (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})
写成生成函数形式就是
然后就可以从 (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;
}