生成函数和组合数学的灵活应用
题意:求一个数列的$ k$次前缀和
$ Solution:$
我们对原数列$ a$建生成函数$ A=sumlimits_{i=0}^{n-1} a_ix^i $
求一次前缀和相当于将$ A$卷上生成函数$ B=sumlimits_{i=0}^{n-1}x^i pmod{x^n}$
即我们要求的就是$ A·B^{k-1} pmod{x^n}$
直接快速幂是$ log^2$的,但是生成函数$ B$有一些巧妙的性质:
$ B^k(x)$的意义是选$ k$个自然数使得和为$ x$
可以通过插板法得知$ B^k(x)$=$ C_{x+k}^x$
然后求出$ A,B$之后$ NTT$卷积即可
时间复杂度:$ O(n log n)$
$ my code:$
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define p 998244353 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x = 0; char zf = 1; char ch = getchar(); while (ch != '-' && !isdigit(ch)) ch = getchar(); if (ch == '-') zf = -1, ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar(' ');} int i,j,k,n,x,y,z,cnt,lim,m; vector<int>A,B,R; int inv[100010]; int ksm(int x,int y){ int ans=1; for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p; return ans; } void init(){ lim=1;while(lim<=n+n)lim*=2; A.resize(lim);B.resize(lim);R.resize(lim); for(rt i=1;i<lim;i++)R[i]=(R[i>>1]>>1)|(i&1)*(lim>>1); for(rt i=0;i<n;i++)A[i]=read(); inv[0]=inv[1]=1;B[0]=1; for(rt i=2;i<=n;i++)inv[i]=1ll*inv[p%i]*(p-p/i)%p; for(rt i=1;i<n;i++)B[i]=1ll*(i+m)*B[i-1]%p*inv[i]%p; } void NTT(int n,vector<int>&A,int fla){ for(rt i=0;i<n;i++)if(i>R[i])swap(A[i],A[R[i]]); for(rt i=1;i<n;i<<=1){ int w=ksm(3,(p-1)/2/i); for(rt j=0;j<n;j+=i<<1){ int K=1; for(rt k=0;k<i;k++,K=1ll*K*w%p){ int x=A[j+k],y=1ll*K*A[i+j+k]%p; A[j+k]=(x+y)%p,A[i+j+k]=(x-y)%p; } } } if(fla==-1){ reverse(A.begin()+1,A.end());int invn=ksm(n,p-2); for(rt i=0;i<n;i++)A[i]=1ll*A[i]*invn%p; } } int main(){ n=read();m=(read()-1)%p;init(); NTT(lim,A,1);NTT(lim,B,1); for(rt i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%p; NTT(lim,A,-1); for(rt i=0;i<n;i++)writeln((A[i]+p)%p); return 0; }