多项式半家桶,但是未封装

多项式乘法逆

题意:

给定 \(n-1\) 次多项式 \(F(x)\) ,求多项式 \(G(x)\),使得 \(F(x) G(x) \equiv 1 \pmod {x^n}\)

思路:

设 :

\[F(x) g(x) \equiv 1 \pmod {x^m} \\ \ \\ F(x) G(x) \equiv 1 \pmod {x^{2m}} \]

已知 \(g(x)\) ,考虑如何求解 \(G(x)\)

因为:

\[F(x) G(x) \equiv 1\pmod {x^{2m}} \]

因此有:

\[F(x) G(x) \equiv 1 \pmod {x^{m}} \]

两者相减,于是有:

\[F(x)(G(x)-g(x)) \equiv 0 \pmod {x^{m}} \\ \ \\ G(x)-g(x) \equiv 0 \pmod {x^{m}} \]

再平方(注意此时模数变为了原来的二倍):

\[(G(x)-g(x) )^2\equiv 0 \pmod {x^{2m}} \\ \ \\ G(x)^2-2G(x) g(x)+g(x)^2\equiv 0 \pmod {x^{2m}} \]

左右两边同乘 \(F(x)\)

\[F(x)G(x)^2-2F(x)G(x) g(x)+F(x)g(x)^2\equiv 0 \pmod {x^{2m}} \\ \ \\ G(x)-2 g(x)+F(x)g(x)^2\equiv 0 \pmod {x^{2m}} \\ \ \\ G(x) \equiv 2 g(x)-F(x)g(x)^2 \pmod {x^{2m}} \\ \ \\ G(x) \equiv g(x)(2-F(x)g(x)) \pmod {x^{2m}} \\ \]

\(g_0\) 要初始化成 \(F_0\) 的乘法逆元,递推或者递归求解 \(G(x)\)

注意事项:

  1. 边界问题,即 \(F(x)\) 应该取到 \(2m-1\) 次方, \(g(x)\) 应该取到 \(m-1\) 次方,求得的 \(G(x)\) 应该取到 \(2m-1\) 次方(超出的设为 \(0\)) ,进行多项式乘法时至少要使长度 \(lim > 4m\)

  2. 递归求解时要先求 \(\bmod \lceil \frac{n}{2}\rceil\) 意义下的 \(g(x)\) ,再求 \(\bmod n\) 意义下的 \(G(x)\)


多项式开根

题意:

给定 \(n-1\) 次多项式 \(F(x)\) ,求多项式 \(G(x)\),使得 \(G(x)^2 \equiv F(x) \pmod {x^n}\)

保证 \(F_0=1\)

思路:

延续着多项式求逆倍增的思路,我们依然设:

\[g(x) ^2\equiv F(x) \pmod {x^m} \\ \ \\ G(x) ^2\equiv F(x) \pmod {x^{2m}} \]

已知 \(g(x)\) ,考虑如何求解 \(G(x)\)

因为:

\[ G(x)^2 \equiv F(x)\pmod {x^{2m}} \]

因此有:

\[ G(x)^2 \equiv F(x) \pmod {x^{m}} \\ \ \\ G(x) \equiv g(x) \pmod {x^{m}} \\ \ \\ G(x)-g(x) \equiv 0 \pmod {x^{m}} \]

两边平方:

\[ (G(x)-g(x))^2 \equiv 0 \pmod {x^{2m}} \\ \ \\ G(x)^2-2G(x)g(x)+g(x)^2 \equiv 0 \pmod {x^{2m}} \]

\(F(x)\) 替换 \(G(x)^2\)

\[ F(x)-2G(x)g(x)+g(x)^2 \equiv 0 \pmod {x^{2m}} \\ \ \\ 2G(x)g(x) \equiv F(x)+g(x)^2 \pmod {x^{2m}} \\ \ \\ G(x) \equiv \frac{F(x)+g(x)^2}{2g(x)} \pmod {x^{2m}} \]

初始化 \(g_0=1\),递推或递归求解 \(G(x)\)

注意事项跟多项式求逆差不多。

但如果不封装的话,注意求逆时数组混用的问题。

\(F_0 \ne 1\) 时,需要求解 \(F_0\)\(\bmod p\) 意义下的二次剩余作为初始值。


多项式求导

给定 \(n-1\) 次多项式 \(F(x)\) ,求 \(F'(x) \pmod {x^n}\)

因为

\[(x^n)'=nx^{n-1} \]

所以 :

\[ F'(x)=\sum_{i=0}^{n-2} (i+1) F_{i+1} x^i \]

code
inline void Diff(ll *a,int len){
    for(Reg int i=0;i<len-1;++i) a[i]=a[i+1]*(i+1)%mod; 
    a[len-1]=0;
}

多项式积分

给定 \(n-1\) 次多项式 \(F(x)\) ,求 \(F(x)\) 的不定积分 \(G(x)\)

因为:

\[\int x_a dx= \frac{1}{a+1} x^{a+1} \]

所以

\[G(x)=\sum_{i=1}^{n-1} \frac{ G_{i-1} } {i} x^i \]

code
inline void Inte(ll *a,int len){
    for(Reg int i=len-1;i>0;--i) a[i]=a[i-1]*qpow(i,mod-2)%mod; 
    a[0]=0;
}


多项式对数函数 (ln)

题意:

给定 \(n-1\) 次多项式 \(F(x)\) ,求多项式 \(G(x)\),使得 \(G(x) \equiv \ln {F(x)} \pmod {x^n}\)

保证 \(F_0=1\)

思路:

考虑同时对两边求导,有:

\[G'(x) \equiv \frac{F'(x)}{F(x)} \pmod {x^n} \]

再同时对两边做不定积分:

\[\int G'(x) \equiv \int \frac{F'(x)}{F(x)} \pmod {x^n} \\ \ \\ G(x) \equiv \int \frac{F'(x)}{F(x)} \pmod {x^n} \]

\(F(x)\) 求导和求乘法逆,相乘再积分即可求得 \(G(x)\)


多项式指数函数 (exp)

题意:

给定 \(n-1\) 次多项式 \(F(x)\) ,求多项式 \(G(x)\),使得 \(G(x) \equiv e^{F(x)} \pmod {x^n}\)

保证 \(F_0=0\)

思路:

不会牛顿迭代,麻。

对两边求导:

\[G'(x) \equiv F'(x)e^{F(x)} \pmod {x^n} \]

\(G(x)\)\(e^{F(x)}\) 替换:

\[G'(x) \equiv F'(x)G(x) \pmod {x^n} \]

对两边做不定积分:

\[\int G'(x) \equiv \int F'(x)G(x) \pmod {x^n} \\ \ \\ G(x) \equiv \int F'(x)G(x) \pmod {x^n} \]

\(T(x)=F'(x)G(x)\) ,那么对于 \(G(x)\) 的第 \(i\) 项系数 \(G_i\) 有:

\[G_i=\frac{ T_{i-1}}{i} \]

也就是:

\[G_i=\frac{ \sum_{j=0}^{i-1} F_j G_{i-1-j}}{i} \]

然后就可以半在线卷积了,复杂度 \(O(n\log ^2 n)\)

留坑待补。

好了来补了。


泰勒展开

\(f^{(i)}(x)\)\(f(x)\)\(i\) 阶导,定义函数 \(f(x)\)\(x_0\)的泰勒展开式如下:

\[F(x)=\sum_{i=0} \frac{f^{(i)}(x_0)}{i!} (x-x_0)^i \]

可以理解为通过多项式函数 \(F(x)\)\(x_0\) 处去模仿 \(f(x)\) 的导数,以无限逼近 \(f(x)\)

牛顿迭代法

牛顿迭代法可以求出单调函数零点的近似解。

对于一个函数 \(f(x)\) ,已知其在 \([a,b]\) 上连续且单调。 设 \(x_0\) 为一个 \(f(x)=0\) 的近似解。

假设从 \(x_i\) 开始,我们每次在 \(f(x_i)\) 的位置作一个关于 \(f(x)\) 的切线,显然,切线与 \(x\) 轴相交的位置则是一个比 \(x_i\) 更靠近零点的位置 \(x_{i+1}\) 。根据导数的几何意义有:

\[f'(x_i)=\frac{f(x_i)}{x_i-x_{i+1}} \]

整理一下可得:

\[x_{i+1}=x_i-\frac{f(x_i)}{f'(x_i)} \]

多项式牛顿迭代

\(G(f(x)) = 0 \pmod {x^m} ,G(F(x)) =0 \pmod {x^{2m}}\) ,则有 $F(x) \equiv f(x)-\frac{G(f(x))}{G'(f(x))} \pmod {x^{2m}} $

类比普通函数应该可以感性理解(

不那么感性的证明:

首先应该有 $F(x)\equiv f(x) \pmod {x^{m}} $ 。

我们有 \(G(F(x))\)\(f(x)\) 处的泰勒展开式为:

\[G(F(x)) = G(f(x)) + \frac{G'(f(x))}{1!} (F(x)-f(x)) + \frac{G''(f(x))}{2!} (F(x)-f(x))^2 ... \pmod {x^{2m}} \]

\(F(x)\)\(f(x)\) 作差之后只会保留后 \(m\) 项,也就是最低次数为 \(m\) ,而 \((F(x)-f(x))^2\) 以后的最低次数至少为 \(2m\) ,在模 \(x^{2m}\) 意义下与 \(0\) 同余,于是:

\[G(F(x)) = G(f(x)) + \frac{G'(f(x))}{1!} (F(x)-f(x)) \pmod {x^{2m}} \]

\(0\) 替换 \(G(F(x))\),整理后的式子即为 :

\[F(x) \equiv f(x)-\frac{G(f(x))}{G'(f(x))} \pmod {x^{2m}} \]

可以通过倍增求解。


回归题意,我们要求的是:

\[G(x) \equiv e^{F(x)} \pmod {x^n} \]

两边取一下 \(\ln\) 有:

\[\ln G(x) \equiv F(x) \pmod {x^n} \\ \ \\ \ln G(x) -F(x) \equiv 0 \pmod {x^n} \]

\(H(G(x)) = \ln G(x) -F(x) \pmod {x^n}\) ,要求的就是 \(H(x)\) 的零点,使用牛顿迭代法:

\[G(x) \equiv g(x)-\frac{\ln g(x) -F(x)}{\frac{1}{g(x)}} \pmod {x^{2m}} \\ \ \\ G(x) \equiv g(x)(1-\ln g(x) +F(x)) \pmod {x^{2m}} \]

倍增过程中进行多项式 \(\ln\) 和多项式乘法即可。

复杂度为 \(O(n\log n)\) ,但是常数比半在线卷积大。


多项式快速幂

题意:

给定 \(n-1\) 次多项式 \(F(x)\) 与整数 \(k\) 。求多项式 \(G(x)\),使得 \(G(x) \equiv \ {F(x)}^k \pmod {x^n}\)

其中 $ 0\le k\le 10^{100000}, F_0=1 $

思路:

首先想的应该是对指数取模,直接模 \(mod\) 即可。

这时好像可以写倍增,但可以有复杂度更优的做法,考虑转化题意。

对两边取自然对数:

\[\ln G(x) \equiv \ln F(x)^k \pmod {x^n} \\ \ \\ \ln G(x) \equiv k\ln F(x) \pmod {x^n} \\ \]

然后再取指数:

\[G(x) \equiv e^{k\ln F(x)} \pmod {x^n} \]

于是一次 \(\ln\) 一次 \(\exp\) 就做完了。

当 $F_0=1 $ 时, $ \ln F_0=0$ ,所以 \(\exp\) 时 $G_0 $ 应该设为 \(1\)

\(F_0\ne 1\) 时的 \(G_0\) 应为 $F_0^k \bmod mod $ ,特别注意 \(F_0=0\) 时没有 \(\ln F_0\) (不存在),因此要找到一个系数为 \(0\) 的前缀长度 \(len\) ,乘上 \(k\) 即为 \(G(x)\) 前缀 为 \(0\) 的长度。




代码

code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Reg register
#define ll long long
using namespace std;
const int maxn=411000;
const int G=3,mod=998244353;
int n,p,R[maxn];
ll A[maxn],B[maxn],C[maxn],D[maxn],E[maxn];
ll f[maxn],g[maxn];
inline ll read(int typ){
    ll s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        if(typ) s=(s*10%mod+(ch^48))%mod;
        else s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    return s*w;
}
inline ll qpow(ll A,ll B){
    ll Ans=1;
    while(B){
        if(B&1) Ans=Ans*A%mod;
        A=A*A%mod;
        B>>=1;
    }
    return Ans;
}
inline ll upd(ll A,ll B){
    A+=B;
    return A>=mod?A-mod:A;
}
inline ll dow(ll A,ll B){
    A-=B;
    return A<0?A+mod:A;
}
inline void Getr(int len,int L){
    for(Reg int i=0;i<len;++i) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
}
inline void NTT(ll *a,int lim,ll typ){
    for(Reg int i=0;i<lim;++i){
        if(i<R[i]) swap(a[i],a[R[i]]);
    } 
    for(Reg int j=1,p=2;j<lim;j<<=1,p<<=1){
        ll t=qpow(G,(typ*((mod-1)/p)+mod-1)%(mod-1));
        for(Reg int k=0;k<lim;k+=p){
            ll T=1;
            for(Reg int l=0;l<j;++l,T=T*t%mod){
                ll Nx=a[k+l],Ny=T*a[k+l+j]%mod;
                a[k+l]=upd(Nx,Ny);
                a[k+l+j]=dow(Nx,Ny);
            }
        }
    }
    if(typ==-1){
        ll r=qpow(lim,mod-2);
        for(Reg int i=0;i<lim;++i) a[i]=a[i]*r%mod;
    }
}
inline void Diff(ll *a,int len){
    for(Reg int i=0;i<n-1;++i) a[i]=a[i+1]*(i+1)%mod; 
    a[len-1]=0;
}
inline void Inte(ll *a,int len){
    for(Reg int i=n-1;i>0;--i) a[i]=a[i-1]*qpow(i,mod-2)%mod; 
    a[0]=0;
}
inline void Getinv(ll *a,ll *b,int len){
    b[0]=qpow(a[0],mod-2),void();
    int lim=1,L=0,lstlim=0;
    while(lim<2*len){
        lstlim=lim;
        lim<<=1,++L,Getr(lim,L);
        memset(D,0,sizeof(ll)*(lim+10));
        memset(E,0,sizeof(ll)*(lim+10));
        for(Reg int i=0;i<lstlim;++i) D[i]=a[i],E[i]=b[i];
        NTT(D,lim,1),NTT(E,lim,1);
        for(Reg int i=0;i<lim;++i) D[i]=(2-D[i]*E[i]%mod+mod)%mod*E[i]%mod;
        NTT(D,lim,-1);
        for(Reg int i=0;i<lstlim;++i) b[i]=D[i];
    }
}
inline void Getsqrt(ll *a,ll *b,int len){
    b[0]=1;
    int lim=1,L=0,lstlim=0;
    while(lim<2*len){
        lstlim=lim;
        lim<<=1,++L,Getr(lim,L);
        memset(A,0,sizeof(ll)*(lim+10));
        memset(B,0,sizeof(ll)*(lim+10));
        memset(C,0,sizeof(ll)*(lim+10));
        for(Reg int i=0;i<lstlim;++i) A[i]=a[i],B[i]=b[i];
        NTT(A,lim,1),NTT(B,lim,1);
        for(Reg int i=0;i<lstlim;++i) b[i]=2*b[i]%mod;
        Getinv(b,C,lstlim);
        NTT(C,lim,1);
        for(Reg int i=0;i<lim;++i) A[i]=(A[i]+B[i]*B[i]%mod+mod)%mod*C[i]%mod;
        NTT(A,lim,-1);
        for(Reg int i=0;i<lstlim;++i) b[i]=A[i];
    }
}
inline void Ln(ll *a,ll *b,int len){
    Getinv(a,C,len),Diff(a,len);
    int lim=1,L=0;
    while(lim<=2*len) lim<<=1,++L;Getr(lim,L);
    NTT(a,lim,1),NTT(C,lim,1);
    for(Reg int i=0;i<lim;++i) a[i]=a[i]*C[i]%mod;
    NTT(a,lim,-1);
    for(Reg int i=0;i<len;++i) b[i]=a[i];
    Inte(b,len);
}
inline void calc(ll *a,ll *b,int lim){
    NTT(a,lim,1),NTT(b,lim,1);
    for(Reg int i=0;i<lim;++i)  a[i]=a[i]*b[i]%mod;
    NTT(a,lim,-1);
}
inline void Solve(ll *a,ll *b,int l,int r){
    if(l==r){
         //printf("%lld\n",f[l]);
        if(l) a[l]=a[l]*qpow(l,mod-2)%mod;
        else a[l]=1;
        return;
    }
    int mid=(l+r)>>1;
    Solve(a,b,l,mid);
    int up=r-l+1,lim=1,L=0;
    while(lim<=2*up) lim<<=1,++L;Getr(lim,L);
    memset(A,0,sizeof(ll)*(lim+10));
    memset(B,0,sizeof(ll)*(lim+10));
    for(Reg int i=0;i<mid-l+1;++i) A[i]=a[i+l];
    for(Reg int i=1;i<=r-l;++i) B[i]=b[i-1];
    calc(A,B,lim);
    for(Reg int i=mid-l+1;i<=r-l;++i) a[i+l]=(a[i+l]+A[i])%mod;
    Solve(a,b,mid+1,r);
} 
inline void Exp(ll *a,ll *b,int len){
    Diff(a,len);
    Solve(b,a,0,len-1);
}
/*
inline void Exp(ll *a,ll *b,int len){
    b[0]=1;
    int lim=1,L=0;
    while(lim<=2*len){
        lim<<=1,++L;
        memset(A,0,sizeof(ll)*(lim+10));
        memset(B,0,sizeof(ll)*(lim+10));
        memset(H,0,sizeof(ll)*(lim+10));
        for(Reg int i=0;i<(lim>>1);++i) A[i]=a[i],B[i]=b[i];
        Ln(b,H,(lim>>1)),Getr(lim,L);
        NTT(A,lim,1),NTT(B,lim,1),NTT(H,lim,1);
        for(Reg int i=0;i<lim;++i) A[i]=B[i]*((1-H[i]+mod+A[i])%mod)%mod;
        NTT(A,lim,-1);
        for(Reg int i=0;i<(lim>>1);++i) b[i]=A[i]; 
    }
}

*/
inline void Polyqpow(ll *a,ll *b,ll p,int len){
    Ln(a,a,len);
    for(Reg int i=0;i<len;++i) a[i]=a[i]*p%mod;
    Exp(a,b,len);
}
int main(){
    n=read(0);//p=read(1);
    for(Reg int i=0;i<n;++i) f[i]=read(0);
    //Getinv(f,g,n),Getsqrt(f,g,n),Ln(f,g,n),Exp(f,g,n),Polyqpow(f,g,p,n);
    for(Reg int i=0;i<n;++i) printf("%lld ",g[i]);
    printf("\n");
    return 0;
}

posted @ 2023-01-11 20:55  Broken_Eclipse  阅读(49)  评论(4编辑  收藏  举报

Loading