题解 P4726 【【模板】多项式指数函数】

· · 题解

orz这题太珂怕了……似乎都找不到几个板子参(lai)考(chao)……前置芝士又特别多……而且我写的时候牛顿迭代那里NTT数组长度写错了调了半天……然后各种地方多项式没清零又调了半天……可能是因为平时都抄板子的缘故没注意这问题……

前置芝士:多项式对数函数(这里),泰勒展开(可以看看这里第一个回答,非常……生动形象),牛顿迭代法(这里)

泰勒展开

简单来说的话,就是要求一个函数f(x)某一点上的值,我们可以构造一个函数g(x),那么对于x点的值,有f(x)\approx g(x)=g(0)+\frac{f^1(0)}{1!}x+\frac{f^2(0)}{2!}x^2+……+\frac{f^n(0)}{n!}x^n

其中f^n(0)表示对原函数图像上0这个点进行n阶求导

牛顿迭代

牛顿迭代可以用来求一个函数的零点,多项式牛顿迭代自然是用来求多项式的零点的,即对于一个函数G(x),求满足条件G(F(z)) \equiv 0 \pmod {z^n}的多项式F(z)

miskcoo大佬是这么说的

扯远了

然后现在就是要计算F(x)=e^{A(x)}

那么变形一下得\ln F(x)-A(x)=0

我们设G(F(x))=\ln F(x)-A(x),那么就是要求这一个函数的零点。那么我们把F(x)看做变量,A(x)看做常数(我也不知道为什么能这样),对这个进行求导,得G'(F(x))=\frac{1}{F(x)}

那么代入上面牛顿迭代的公式得F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}\pmod{x^n}

F(x)\equiv F_0(x)(1-\ln F_0(x)+A(x))\pmod{x^n}

然后因为A(0)=0,所以F(x)的常数项为1

ps:这里的F_0(x)指的是在模x^{n\over 2}意义下的答案

然后左转把各种板子复制过来就好了

//minamoto
#include<iostream>
#include<cstdio>
#include<algorithm>
#define swap(x,y) (x^=y,y^=x,x^=y)
#define mul(x,y) (1ll*x*y%P)
#define add(x,y) (x+y>=P?x+y-P:x+y)
#define dec(x,y) (x-y<0?x-y+P:x-y)
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int read(){
    #define num ch-'0'
    char ch;bool flag=0;int res;
    while(!isdigit(ch=getc()))
    (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getc());res=res*10+num);
    (flag)&&(res=-res);
    #undef num
    return res;
}
char sr[1<<21],z[20];int K=-1,Z;
inline void Ot(){fwrite(sr,1,K+1,stdout),K=-1;}
inline void print(int x){
    if(K>1<<20)Ot();if(x<0)sr[++K]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++K]=z[Z],--Z);sr[++K]=' ';
}
const int N=500005,P=998244353;
inline int ksm(int a,int b){
    int res=1;
    while(b){
        if(b&1) res=mul(res,a);
        a=mul(a,a),b>>=1;
    }
    return res;
}
int n,r[N],A[N],B[N],C[N],D[N],F[N],G[N],O[N],f[N],g[N],inv[N];
void init(int limit){
    for(int i=0,l=limit<<1;i<=l;++i) inv[i]=ksm(i,P-2);
}
void NTT(int *A,int type,int len){
    int limit=1,l=0;
    while(limit<len) limit<<=1,++l;
    for(int i=0;i<limit;++i)
    r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<limit;++i)
    if(i<r[i]) swap(A[i],A[r[i]]);
    for(int mid=1;mid<limit;mid<<=1){
        int R=mid<<1,Wn=ksm(3,(P-1)/R);O[0]=1;
        for(int j=1;j<mid;++j) O[j]=mul(O[j-1],Wn);
        for(int j=0;j<limit;j+=R){
            for(int k=0;k<mid;++k){
                int x=A[j+k],y=mul(O[k],A[j+k+mid]);
                A[j+k]=add(x,y),A[j+k+mid]=dec(x,y);
            }
        }
    }
    if(type==-1){
        reverse(A+1,A+limit);
        for(int i=0,invl=inv[limit];i<limit;++i)
        A[i]=mul(A[i],invl);
    }
}
void Inv(int *a,int *b,int len){
    if(len==1) return (void)(b[0]=inv[a[0]]);
    Inv(a,b,len>>1);
    for(int i=0;i<len;++i) C[i]=a[i],D[i]=b[i];
    NTT(C,1,len<<1),NTT(D,1,len<<1);
    for(int i=0,l=(len<<1);i<l;++i) C[i]=mul(mul(C[i],D[i]),D[i]);
    NTT(C,-1,len<<1);
    for(int i=0;i<len;++i) b[i]=dec(add(b[i],b[i]),C[i]);
    for(int i=0,l=(len<<1);i<l;++i) C[i]=D[i]=0;
}
void Direv(int *A,int *B,int len){
    for(int i=1;i<len;++i) B[i-1]=mul(A[i],i);B[len-1]=0; 
}
void Inter(int *A,int *B,int len){
    for(int i=1;i<len;++i) B[i]=mul(A[i-1],inv[i]);B[0]=0; 
}
void Ln(int *a,int *b,int len){
    Direv(a,A,len),Inv(a,B,len);
    NTT(A,1,len<<1),NTT(B,1,len<<1);
    for(int i=0,l=len<<1;i<l;++i) A[i]=mul(A[i],B[i]);
    NTT(A,-1,len<<1),Inter(A,b,len<<1);
    for(int i=0,l=len<<1;i<l;++i) A[i]=B[i]=0;
}
void Exp(int *a,int *b,int len){
    if(len==1) return (void)(b[0]=1);
    Exp(a,b,len>>1),Ln(b,F,len);
    F[0]=dec(a[0]+1,F[0]);
    for(int i=1;i<len;++i) F[i]=dec(a[i],F[i]);
    NTT(F,1,len<<1),NTT(b,1,len<<1);
    for(int i=0,l=len<<1;i<l;++i) b[i]=mul(b[i],F[i]);
    NTT(b,-1,len<<1);
    for(int i=len,l=(len<<1);i<l;++i) b[i]=F[i]=0;
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    for(int i=0;i<n;++i) f[i]=read();
    int len;for(len=1;len<=n;len<<=1);init(len);
    Exp(f,g,len);
    for(int i=0;i<n;++i) print(g[i]);
    Ot();
    return 0;
}