多項式的ln、exp、快速冪和開方學習小記

  • 不妨又學習了一下多項式的求ln、exp、快速冪和開方操作。

  • 這些操作比之前的求逆更上了一層臺階,應用同樣很廣。

多項式ln

  • 給出一個次數界爲 nn 的多項式 F(x)F(x) ,要求多項式 G(x)G(x) 滿足:G(x)ln F(x) (mod xn)G(x)\equiv ln\ F(x)\ (mod\ x^n)

  • 我們對其兩邊求導(右邊相當於是複合函數求導):G(x)=F(x)F(x)G'(x)=\frac{F'(x)}{F(x)}

  • 再兩邊積分,即:G(x)=F(x)F(x) dxG(x)=\int\frac{F'(x)}{F(x)}\ dx

  • 於是應用一下多項式求逆即可在 O(n log n)O(n\ log\ n) 的時間內求出一個多項式的 ln 了。

  • 其中求導和積分都是 O(n)O(n) ,具體來說就是:(xa)=axa1(x^a)'=ax^{a-1}xa dx=1a+1xa+1\int{x^a}\ dx=\frac{1}{a+1}x^{a+1}

  • 逐項操作即可求導、積分。

多項式exp

  • 給出一個次數界爲 nn 的多項式 F(x)F(x) ,要求多項式 G(x)G(x) 滿足:G(x)eF(x) (mod xn)G(x)\equiv e^{F(x)}\ (mod\ x^n)

  • 這個需要用到 牛頓迭代法——一種在實數域和複數域上近似求解方程的方法。

  • 牛頓迭代法的一般式如下(要求的根爲 xx):x=xf(x)f(x)x'=x-\frac{f(x)}{f'(x)}

  • 於是我們先將定義式移項:G(x)eF(x)=0G(x)-e^{F(x)}=0

  • 兩邊取對數:ln G(x)F(x)=0ln\ G(x)-F(x)=0

  • G(x)G(x) 當做要求的方程的根,則根據牛頓迭代法可得:Gt+1(x)=Gt(x)ln Gt(x)F(x)1Gt(x)=Gt(x)(1+F(x)ln Gt(x))G_{t+1}(x)=G_t(x)-\frac{ln\ G_t(x)-F(x)}{\frac{1}{G_t(x)}}=G_t(x)(1+F(x)-ln\ G_t(x))

  • 這樣每次迭代都使精度(次數界)翻倍,遞歸處理即可,每次遞歸過程中要進行一次多項式求ln 。

  • 這樣的時間複雜度是 T(n)=T(n2)+O(n log n)=O(n log n)T(n)=T(\frac{n}{2})+O(n\ log\ n)=O(n\ log\ n) 的,不過常數較大。

多項式快速冪

  • 給出一個次數界爲 nn 的多項式 F(x)F(x) ,要求多項式 G(x)G(x) 滿足:G(x)F(x)k (mod xn)G(x)\equiv F(x)^k\ (mod\ x^n)

  • 如果像普通一樣每次直接 NTT 的話,複雜度會很不理想,特別是當 kk 大到一定程度的時候。

  • 我們可以兩邊取對數,則有:ln G(x)=ln F(x)k=k ln F(x)ln\ G(x)=ln\ F(x)^k=k\ ln\ F(x)

  • 右邊多項式係數直接乘 kk ,再兩邊exp還原即可。

  • 這樣做的話 kk 的值可以很大,我們只需要知道它模意義下的值即可。

  • 這樣多項式快速冪需要用到求ln、exp,但複雜度仍是 O(n log n)O(n\ log\ n) 的。

  • 附模板題和我的代碼: 洛谷 P5245 【模板】多項式快速冪

  • 值得注意的是這裏的 kk 達到了 1010510^{10^5} 數量級,我們用高精度除單精度求其模意義下的值即可。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5,L=N<<2,G=3,mo=998244353;
int a[L],b[L],c[L];
int f[L],rev[L],wn[L],inv[L];
char s[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int ksm(int x,int y)
{
    int s=1;
    while(y)
    {
        if(y&1) s=(LL)s*x%mo;
        x=(LL)x*x%mo;
        y>>=1;
    }
    return s;
}
inline void NTT(int *y,int len,int ff)
{
    for(int i=0;i<len;i++)
        if(i<rev[i]) swap(y[i],y[rev[i]]);
    for(int h=2,d=len>>1;h<=len;h<<=1,d>>=1)
        for(int i=0,k=h>>1;i<len;i+=h)
            for(int j=0,cnt=0;j<k;j++,cnt+=d)
            {
                int u=y[i+j],t=(LL)y[i+j+k]*wn[cnt]%mo;
                y[i+j]=u+t>=mo?u+t-mo:u+t;
                y[i+j+k]=u-t<0?u-t+mo:u-t;
            }
    if(ff==-1)
    {
        reverse(y+1,y+len);
        int invl=inv[len];
        for(int i=0;i<len;i++) y[i]=(LL)y[i]*invl%mo;
    }
}
void getinv(int len,int num)
{
    if(len==1)
    {
        b[0]=ksm(c[0],mo-2);
        return;
    }
    getinv(len>>1,num-1);
    for(int i=0;i<len>>1;i++) f[i]=c[i];
    for(int i=len>>1;i<len;i++) f[i]=0;
    for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<num-1;
    int w0=ksm(G,(mo-1)/len);
    for(int i=wn[0]=1;i<=len;i++) wn[i]=(LL)wn[i-1]*w0%mo;
    NTT(f,len,1),NTT(b,len,1);
    for(int i=0;i<len;i++) b[i]=(2-(LL)b[i]*f[i]%mo+mo)*b[i]%mo;
    NTT(b,len,-1);
    for(int i=len>>1;i<len;i++) b[i]=0;
}
inline void getln(int len,int num)
{
    getinv(len,num);
    for(int i=0;i<len-1;i++) f[i]=(LL)c[i+1]*(i+1)%mo;
    f[len-1]=0;
    for(int i=len>>1;i<len;i++) f[i]=b[i]=0;
    NTT(f,len,1),NTT(b,len,1);
    for(int i=0;i<len;i++) f[i]=(LL)f[i]*b[i]%mo;
    NTT(f,len,-1);
    for(int i=len-1;i>0;i--) f[i]=(LL)f[i-1]*inv[i]%mo;
    f[0]=0;
}
void getexp(int len,int num)
{
    if(len==1)
    {
        c[0]=1;
        return;
    }
    getexp(len>>1,num-1);
    for(int i=0;i<len;i++) f[i]=b[i]=0;
    getln(len,num);
    f[0]=(a[0]+1-f[0]+mo)%mo;
    for(int i=1;i<len>>1;i++) f[i]=(a[i]-f[i]+mo)%mo;
    for(int i=len>>1;i<len;i++) f[i]=0;
    NTT(c,len,1),NTT(f,len,1);
    for(int i=0;i<len;i++) c[i]=(LL)c[i]*f[i]%mo;
    NTT(c,len,-1);
    for(int i=len>>1;i<len;i++) c[i]=0;
}
int main()
{
    int n=read(),k=0;
    scanf("%s",s+1);
    int m=strlen(s+1);
    for(int i=1;i<=m;i++) k=((LL)k*10+s[i]-'0')%mo;
    for(int i=0;i<n;i++) c[i]=read();
    inv[0]=inv[1]=1;
    for(int i=2;i<L;i++) inv[i]=(LL)(mo-mo/i)*inv[mo%i]%mo;
    int len=1,num=0;
    while(len<n<<1) len<<=1,num++;
    getln(len,num);
    for(int i=0;i<n;i++) a[i]=(LL)f[i]*k%mo;
    for(int i=0;i<len;i++) f[i]=b[i]=c[i]=0;
    getexp(len,num);
    for(int i=0;i<n;i++) printf("%d ",c[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章