1. 程式人生 > >【洛谷5月月賽】玩遊戲(NTT,生成函數)

【洛谷5月月賽】玩遊戲(NTT,生成函數)

wap class char gist 一個 我們 max 卷積 include

【洛谷5月月賽】玩遊戲(NTT,生成函數)

題面

Luogu

題解

看一下要求的是什麽東西
\((a_x+b_y)^i\)的期望。期望顯然是所有答案和的平均數。
所以求出所有的答案就在乘一個逆元就好了。
現在考慮怎麽算上面那個東西。
對於單個的計算,我們可以用二項式定理直接展開
得到
\[\begin{aligned}\sum(a+b)^k&=\sum\sum_{i=0}^kC_k^ia^ib^{k-i}\\&=\sum_{i=0}^kC_k^i(\sum a^i)(\sum b^{k-i})\\&=\sum_{i=0}^k\frac{k!}{i!(k-i)!}(\sum a^i)(\sum b^{k-i})\\&=k!\sum_{i=0}^k\frac{\sum a^i}{i!}\frac{\sum b^{k-i}}{(k-i)!} \end{aligned}\]


這樣就是很明顯的卷積的形式了。
現在考慮怎麽計算\(\sum a^i\)
構造\(G(x)=\prod_{i=1}^n(1+a_ix)\)
然後對於\(G(x)\)\(ln\),再給第\(i\)項乘上\(i\)就好了。
為什麽?
因為是乘積的形式,所以\(ln\)之後等價於對於所有東西都先\(ln\)在求和
所以考慮一下單個的\(ln\)值是什麽,然後有\(ln'(A(x))=\frac{A'(x)}{A(x)}\)
這個手動用生成函數玩一下就知道了為啥了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MOD 998244353 #define MAX 888888 inline int read() { RG int x=0,t=1;RG char
ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int fpow(int a,int b) { int s=1; while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;} return s; } int r[MAX],W[MAX]; void NTT(int *P,int len,int opt) { int N,l=0; for(N=1;N<len;N<<=1)++l; for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]); for(int i=1;i<N;i<<=1) { int w=fpow(3,(MOD-1)/(i<<1));W[0]=1; for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD; for(int p=i<<1,j=0;j<N;j+=p) for(int k=0;k<i;++k) { int X=P[j+k],Y=1ll*P[i+j+k]*W[k]%MOD; P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD; } } if(opt==-1) { reverse(&P[1],&P[N]); for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD; } } int n,m,t[MAX]; int S1[MAX],S2[MAX]; int inv[MAX]; int F[MAX],P[MAX]; int jc[MAX],jcinv[MAX]; int tmp[50][MAX],St[50],top; void Solve(int l,int r,int *P,int *t) { if(l==r){P[1]=t[l];P[0]=1;return;} int mid=(l+r)>>1,N,ls=St[top--]; Solve(l,mid,tmp[ls],t); int rs=St[top--]; Solve(mid+1,r,tmp[rs],t); for(N=1;N<=r-l+1;N<<=1); NTT(tmp[ls],N,1);NTT(tmp[rs],N,1); for(int i=0;i<N;++i)P[i]=1ll*tmp[ls][i]*tmp[rs][i]%MOD; NTT(P,N,-1); St[++top]=ls;St[++top]=rs; for(int i=0;i<N;++i)tmp[ls][i]=tmp[rs][i]=0; } namespace Poly { int A[MAX],B[MAX]; void Inv(int *a,int *b,int len) { if(len==1){b[0]=fpow(a[0],MOD-2);return;} Inv(a,b,len>>1); for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i]; NTT(A,len<<1,1);NTT(B,len<<1,1); for(int i=0;i<len<<1;++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD; NTT(A,len<<1,-1); for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD; for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD; for(int i=0;i<len<<1;++i)A[i]=B[i]=0; } void Dao(int *a,int *b,int len) { for(int i=1;i<len;++i)b[i-1]=1ll*a[i]*i%MOD; b[len]=b[len-1]=0; } void Jifen(int *a,int *b,int len) { for(int i=1;i<len;++i)b[i]=1ll*a[i-1]*inv[i]%MOD; b[0]=0; } int C[MAX],D[MAX]; void ln(int *a,int *b,int len) { memset(C,0,sizeof(C));memset(D,0,sizeof(D)); Dao(a,C,len);Inv(a,D,len); NTT(C,len<<1,1);NTT(D,len<<1,1); for(int i=0;i<len<<1;++i)C[i]=1ll*C[i]*D[i]%MOD; NTT(C,len<<1,-1);Jifen(C,b,len); } int E[MAX],G[MAX]; void Exp(int *a,int *b,int len) { if(len==1){b[0]=1;return;} Exp(a,b,len>>1);ln(b,E,len); for(int i=0;i<len;++i)E[i]=(MOD-E[i]+a[i])%MOD;E[0]=(E[0]+1)%MOD; for(int i=0;i<len;++i)G[i]=b[i]; NTT(E,len<<1,1);NTT(G,len<<1,1); for(int i=0;i<len<<1;++i)E[i]=1ll*E[i]*G[i]%MOD; NTT(E,len<<1,-1); for(int i=0;i<len;++i)b[i]=E[i]; for(int i=0;i<len<<1;++i)E[i]=G[i]=0; } } int A[MAX],B[MAX],K; int SA[MAX],SB[MAX]; int main() { n=read();m=read(); for(int i=1;i<=n;++i)A[i]=read(); for(int i=1;i<=m;++i)B[i]=read(); K=read(); inv[0]=inv[1]=1; int len;for(len=1;len<=max(n,m)+K;len<<=1); for(int i=2;i<(len<<1);++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=0;i<50;++i)St[++top]=i;Solve(1,n,S1,A);Poly::ln(S1,SA,len);top=0; for(int i=0;i<50;++i)St[++top]=i;Solve(1,m,S2,B);Poly::ln(S2,SB,len);top=0; for(int i=0;i<len;++i)SA[i]=1ll*SA[i]*i%MOD; for(int i=0;i<len;i+=2)SA[i]=(MOD-SA[i])%MOD; for(int i=0;i<len;++i)SB[i]=1ll*SB[i]*i%MOD; for(int i=0;i<len;i+=2)SB[i]=(MOD-SB[i])%MOD; SA[0]=n;SB[0]=m;jc[0]=jcinv[0]=1; for(int i=1;i<len;++i)jc[i]=1ll*i*jc[i-1]%MOD; for(int i=1;i<len;++i)jcinv[i]=1ll*jcinv[i-1]*inv[i]%MOD; memset(A,0,sizeof(A));memset(B,0,sizeof(B)); for(int i=0;i<=K;++i)A[i]=1ll*SA[i]*jcinv[i]%MOD; for(int i=0;i<=K;++i)B[i]=1ll*SB[i]*jcinv[i]%MOD; for(len=1;len<=K+K;len<<=1); NTT(A,len,1);NTT(B,len,1); for(int i=0;i<len;++i)A[i]=1ll*A[i]*B[i]%MOD; NTT(A,len,-1); for(int i=1,inv=fpow(1ll*n*m%MOD,MOD-2);i<=K;++i) { int ans=1ll*A[i]*jc[i]%MOD*inv%MOD; printf("%d\n",ans); } return 0; }

【洛谷5月月賽】玩遊戲(NTT,生成函數)