• CF755G PolandBall and Many Other Balls


    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) 看着就很烦,然而去不掉,而且拆了半天式子没法卷积。

    那就暴力拆阶乘吧!!!

    [ans_i=sum_{j=0}^{i}inom{n-j}{j}inom{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}inom{i}{j}inom{n-j}{i} ]

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

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

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

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

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

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

    然后随便推推就能卷了。

    [sum_{j=0}^{i}(-1)^jinom{i}{j}inom{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}} imes 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})

    写成生成函数形式就是

    [egin{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;
    }
    
  • 相关阅读:
    memset使用技巧
    04.碰撞反应
    03.键盘状态跟踪与精灵删除
    02.基本动作
    01.基本图形
    00.入门
    03.交互--鼠标,键盘
    02.action--新增精灵知识点
    01.helloworld--标签
    05.声音
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14191386.html
Copyright © 2020-2023  润新知