LOJ #6261 一个人的高三楼

生成函数和组合数学的灵活应用

LOJ #6261

题意:求一个数列的$ 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;
}
原文地址:https://www.cnblogs.com/DreamlessDreams/p/10056752.html