[P5162] WD與積木
阿新 • • 發佈:2019-02-10
ret sca 。。 main wap 開始 code n) ++i
每種堆法(理解成名次序列,舉例3,3,8,2和7,7,100,2都對應2,2,1,3這個名次序列)等概率出現。
考慮一個名次的生成函數應為F(x)=sum i=1->inf x^i/(i!) (Ge(x)=F(x)+1=e^x)。因為F(x)的常數項為0,有F^inf(x)=0。
推及:
名次序列的方案數的生成函數:A(x) = sum i=0->inf F^i(x) = 1/(1-F(x))
名次序列的各種方案的名次個數之和的生成函數:B(x) = sum i=0->inf i*F^i(x) = F(x)/(1-F(x))^2 = A^2(x)-A(x) = A(x)*(A(x)-1)
預處理出A(x),B(x)的前MAX_N項系數,對於n,答案為B(x)[n]/A(x)[n]。
Q: 關於從0開始求和。。smg ??
A: 如果是從1開始的情形:
A(x)=sum i=1->inf F^i(x)
= F(x)*(1-F^inf(x))/(1-F(x))
= F(x)/(1-F(x))
= (1-(1-F(x)))/(1-F(x))
= 1/(1-F(x))-1
B(x)=sum i=1-> inf i*F^i(x)
= ...
= F(x)/(1-F(x))^2-1
拋開A(x)[0]和B(x)[0],似乎關系並沒有什麽影響。
#include <bits/stdc++.h> using namespace std; const int MAX_N=1e5+10; const int P=998244353,G=3; int a[MAX_N<<2]; int b[MAX_N<<2]; int c[MAX_N<<2]; int f[MAX_N<<2]; int r[MAX_N<<2]; int frr[MAX_N]; int qpow(long long x,int y) { long long c=1; for(; y; y>>=1, x=x*x%P) if(y&1) c=c*x%P; return c; } void ntt(int *a,int lmt,int tp) { for(int i=0; i<lmt; ++i) if(i<r[i]) swap(a[i],a[r[i]]); for(int m=1; m<lmt; m<<=1) { int wm=qpow(G,(P-1)/(m<<1)); if(tp==-1) wm=qpow(wm,P-2); for(int i=0; i<lmt; i+=(m<<1)) { long long w=1, tmp; for(int j=0; j<m; ++j, w=w*wm%P) { tmp=w*a[i+j+m]%P; a[i+j+m]=(a[i+j]-tmp+P)%P; a[i+j]=(a[i+j]+tmp)%P; } } } if(tp==-1) { long long tmp=qpow(lmt,P-2); for(int i=0; i<lmt; ++i) a[i]=tmp*a[i]%P; } } void polyrev(int deg,int *a,int *b) { if(deg==1) { b[0]=qpow(a[0],P-2); return; } polyrev((deg+1)>>1,a,b); int lmt=1, l=0; while(lmt<(deg<<1)) lmt<<=1, ++l; for(int i=0; i<lmt; ++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); for(int i=0; i<deg; ++i) c[i]=a[i]; fill(c+deg,c+lmt,0); ntt(c,lmt,1), ntt(b,lmt,1); for(int i=0; i<lmt; ++i) b[i]=(2LL-1LL*c[i]*b[i]%P+P)%P*b[i]%P; ntt(b,lmt,-1); fill(b+deg,b+lmt,0); } int main() { long long fac=1; for(int i=1; i<MAX_N; ++i) fac=fac*i%P; frr[MAX_N-1]=qpow(fac,P-2); for(int i=MAX_N-1; i; --i) frr[i-1]=1LL*i*frr[i]%P; for(int i=0; i<MAX_N; ++i) a[i]=P-frr[i]; a[0]=(a[0]+2)%P; polyrev(MAX_N,a,b); for(int i=0; i<MAX_N; ++i) a[i]=f[i]=b[i]; int lmt=1; while(lmt<=MAX_N) lmt<<=1; lmt<<=1; a[0]=(a[0]+P-1)%P; ntt(f,lmt,1), ntt(a,lmt,1); for(int i=0; i<lmt; ++i) f[i]=1LL*f[i]*a[i]%P; ntt(f,lmt,-1); int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); printf("%lld\n",1LL*f[n]*qpow(b[n],P-2)%P); } return 0; }
感謝@Weng_Weiji的答疑
[P5162] WD與積木