LOJ6703 小 Q 的序列
阿新 • • 發佈:2020-07-27
小 Q 的序列
小 Q 喜歡在序列上數數。
定義一個序列 \(a_{1\ldots k}\) 的權值為 \(\prod_{i = 1}^k(a_i + i)\)。
現在小 Q 有一個長度為 \(n\) 的序列 \(c_{1\ldots n}\)。
他想知道他的序列的所有 \(2^n - 1\) 個非空子序列的權值和。
由於答案很大,你只需要輸出答案對 \(998244353\) 取模的結果。
\(1 \leq n \leq 100000\)
題解
https://www.cnblogs.com/yyf0309/p/12219139.html
https://www.cnblogs.com/weiyanpeng/p/11801162.html
考慮一下樸素DP方程:
\[f(i,j)=f(i-1,j-1)(j+a_i)+f(i-1,j) \]
注意到這個式子當\(a_i=0\)的時候和第二類Stirling數的遞迴式比較像。
考慮令第二維為\(j'=i-j\),那麼有:
\[f(i,j)=f(i-1,j)(i-j+a_i)+f(i-1,j-1) \]
類似斯特林數考慮組合意義。還是把前\(i\)個元素分到\(j\)個非空集合裡面。新開集合就是\(f(i-1,j-1)\),不新開集合就是\(-jf(i-1,j)\)。與第二類斯特林數不同,每個元素還可以不放進集合裡面產生\(i+a_i\)的貢獻。
分治FFT+exp即可。時間複雜度\(O(n\log^2n)\)
CO int N=1<<18; int omg[2][N],rev[N]; int fac[N],inv[N],ifac[N]; void NTT(poly&a,int dir){ int lim=a.size(),len=log2(lim); for(int i=0;i<lim;++i){ int r=rev[i]>>(18-len); if(i<r) swap(a[i],a[r]); } for(int i=1;i<lim;i<<=1) for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){ int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]); a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t); } if(dir){ int ilim=fpow(lim,mod-2); for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim); } } poly operator~(poly a){ int n=a.size(); poly b={fpow(a[0],mod-2)}; a.resize(1<<(int)ceil(log2(n))); for(int lim=2;lim<2*n;lim<<=1){ poly c(a.begin(),a.begin()+lim); c.resize(lim<<1),NTT(c,0); b.resize(lim<<1),NTT(b,0); for(int i=0;i<lim<<1;++i) b[i]=mul(2+mod-mul(c[i],b[i]),b[i]); NTT(b,1),b.resize(lim); } return b.resize(n),b; } poly log(poly a){ int n=a.size(); poly b=~a; for(int i=0;i<n-1;++i) a[i]=mul(a[i+1],i+1); a.resize(n-1); int lim=1<<(int)ceil(log2(2*n-2)); a.resize(lim),NTT(a,0); b.resize(lim),NTT(b,0); for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]); NTT(a,1),a.resize(n); for(int i=n-1;i>=1;--i) a[i]=mul(a[i-1],inv[i]); assert(n-1<N); return a[0]=0,a; } poly exp(poly a){ int n=a.size(); poly b={1}; a.resize(1<<(int)ceil(log2(n))); for(int lim=2;lim<2*n;lim<<=1){ b.resize(lim);poly c=log(b); c[0]=add(1+a[0],mod-c[0]); for(int i=1;i<lim;++i) c[i]=add(a[i],mod-c[i]); c.resize(lim<<1),NTT(c,0); b.resize(lim<<1),NTT(b,0); for(int i=0;i<lim<<1;++i) b[i]=mul(c[i],b[i]); NTT(b,1),b.resize(lim); } return b.resize(n),b; } poly operator*(poly a,poly b){ int n=a.size()+b.size()-1,lim=1<<(int)ceil(log2(n)); a.resize(lim),NTT(a,0); b.resize(lim),NTT(b,0); for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]); NTT(a,1),a.resize(n); return a; } int a[N]; poly solve(int l,int r){ if(l==r) return poly{1,add(a[l],l)}; int mid=(l+r)>>1; return solve(l,mid)*solve(mid+1,r); } int main(){ omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N); omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2); rev[0]=0,rev[1]=1<<17; fac[0]=fac[1]=1; inv[0]=inv[1]=1; ifac[0]=ifac[1]=1; for(int i=2;i<N;++i){ omg[0][i]=mul(omg[0][i-1],omg[0][1]); omg[1][i]=mul(omg[1][i-1],omg[1][1]); rev[i]=rev[i>>1]>>1|(i&1)<<17; fac[i]=mul(fac[i-1],i); inv[i]=mul(mod-mod/i,inv[mod%i]); ifac[i]=mul(ifac[i-1],inv[i]); } int n=read<int>(); for(int i=1;i<=n;++i) read(a[i]); poly f=solve(1,n),g(n+1); for(int i=1;i<=n;++i) g[i]=mul(ifac[i],(i-1)%2==1?mod-1:1); g=exp(g); int ans=0; for(int i=0;i<=n;++i) ans=add(ans,mul(f[i],mul(g[n-i],fac[n-i]))); write(add(ans,mod-1),'\n'); // empty set return 0; }