【总结】多项式相关的各种算法

文章目录

  • 基本概念
    • 符号约定&说明
    • 求导&积分
    • 点值表示&拉格朗日插值
    • 卷积(Karatsuba算法&FTT&NTT)
    • 牛顿迭代
  • 基本运算&初等函数
    • 求逆
    • 开根
    • ln
    • exp
    • 快速幂
    • 三角函数
    • 除法&取模
    • 形式幂级数&拉格朗日反演
    • 时间复杂度总结
  • 求值&插值
    • 多点求值
    • 快速插值(牛顿插值&拉格朗日优化)
  • 参考资料


基本概念

定义形如这样的函数为多项式:

f ( x ) = ∑ i = 0 n − 1 a i x i f(x)=\sum _{i=0} ^{n-1} a_ix^i f(x)=i=0n1aixi

其中 n n n为多项式的项数(系数为0的项也是项), n − 1 n-1 n1为多项式的最高次幂, a i a_i ai为多项式中 i i i次项的系数。


符号约定&说明

  • 一般情况下,函数自变量均用 x x x表示。
  • f ( x ) f(x) f(x)表示 f f f这个多项式
  • f ( a ) f(a) f(a)表示自变量 x = a ( a 为 给 定 的 数 ) x=a(a为给定的数) x=a(a) f f f的函数值
  • f [ n ] f[n] f[n]表示多项式 f f f x n x^n xn一项的系数
  • d e g ( f ) deg(f) deg(f)表示多项式 f f f的项数,也即最高次幂 − 1 -1 1
  • 求积分时,常数项默认为 0 0 0

求导&积分

求导:

对于某一项 x n x^n xn,求导为 n x n − 1 nx^{n-1} nxn1

h ( x ) = f ′ ( x ) h(x)=f'(x) h(x)=f(x),则 h [ n ] = ( n + 1 ) f [ n + 1 ] ( 0 ≤ n < d e g ( h ) ) h[n]=(n+1)f[n+1](0\leq n<deg(h)) h[n]=(n+1)f[n+1](0n<deg(h)))

inline void getderivative(int n,int *f,int *h)
{
    for(int i=0;i<n;++i) h[i]=mul(f[i+1],i+1);
    h[n]=0;
}

积分:

h ( x ) = ∫ f ( x ) d x h(x)=\int f(x)dx h(x)=f(x)dx,则 h [ n ] = f [ n − 1 ] n ( 0 < n < d e g ( h ) , h [ 0 ] = 0 ) h[n]=\frac {f[n-1]}{n}(0<n<deg(h),h[0]=0) h[n]=nf[n1](0<n<deg(h),h[0]=0)

inline void getintegration(int n,int *f,int *h)
{
    for(int i=1;i<n;++i) h[i]=mul(f[i-1],inv[i]);//inv[i] i的逆元
    h[0]=0;
}

点值表示&拉格朗日插值

拉格朗日插值讲解

多项式 f ( x ) f(x) f(x)不仅可以用每一项系数表示,由拉格朗日插值法,得到也可以用 d e g ( f ) deg(f) deg(f)个二元组 ( x i , f ( x i ) ) ( x i ≠ x j , i ≠ j ) (x_i,f(x_i))(x_i\neq x_j,i\neq j) (xi,f(xi))(xi̸=xj,i̸=j)来表示,也就是用 n n n个点来描述一个 n n n项多项式(即为点值表达式):

f ( x ) ∈ { d e g ( f ) < n 且 ∀ 0 ≤ i < n , f ( x i ) = y i , x i ≠ x j , i ≠ j } f(x)\in \{ deg(f)<n 且 \forall 0\leq i< n,f(x_i)=y_i,x_i\neq x_j,i\neq j\} f(x){deg(f)<n0i<n,f(xi)=yi,xi̸=xj,i̸=j}

这种用点描述多项式的形式称为多项式的点值表示,由点值转化为系数式的过程称为插值。


卷积(Karatsuba算法&FTT&NTT)

多项式之间的乘积称为卷积。

h ( x ) = f ( x ) ∗ g ( x ) ( ∗ 即 为 卷 积 符 号 ) h(x)=f(x)*g(x)(*即为卷积符号) h(x)=f(x)g(x)(),则 h [ n ] = ∑ i = 0 n f [ i ] g [ n − i ] ( 0 ≤ n < d e g ( h ) ) h[n]=\sum _{i=0}^{n} f[i]g[n-i](0\leq n<deg(h)) h[n]=i=0nf[i]g[ni](0n<deg(h))

d e g ( h ) = N deg(h)=N deg(h)=N。直接求多项式卷积,复杂度是 N 2 N^2 N2的,使用分治的 K a r a t s u b a Karatsuba Karatsuba算法可复杂度为 O ( n l o g 2 3 ) = O ( n 1.585 ) O(n^{log_2 3})=O(n^{1.585}) O(nlog23)=O(n1.585),使用 F F T / N T T FFT/NTT FFT/NTT可以将复杂度降到 N l o g N NlogN NlogN

K a r a t s u b a Karatsuba Karatsuba算法原理是利用

     ( A x 2 n + B ) ( C x 2 n + D ) = A C x n + ( ( A + B ) ( C + D ) − A C − B D ) x 2 n + B D \ \ \ \ (Ax^{\frac 2n}+B)(Cx^{\frac 2n}+D)\\=ACx^n+((A+B)(C+D)-AC-BD)x^{\frac 2n}+BD     (Axn2+B)(Cxn2+D)=ACxn+((A+B)(C+D)ACBD)xn2+BD

不断递归减少乘法次数。

F F T / N T T FFT/ NTT FFT/NTT的基本思想都是利用单位复根/单位原根并采用分治策略先将 f ( x ) , g ( x ) f(x),g(x) f(x),g(x)转化为点值表示,在点值上处理为 h ( x ) h(x) h(x)的点值表示(显然 f ( a ) g ( a ) = h ( a ) f(a)g(a)=h(a) f(a)g(a)=h(a)),再转化为 h ( x ) h(x) h(x),因为 F F T FFT FFT中涉及大量虚数实数运算,精度和常数都很感人,所以常用单模数 N T T NTT NTT,但限制模数为质数。

这里假设大家都已经会 F F T , N T T FFT,NTT FFT,NTT,就不详细展开了。

这里先给出 N T T NTT NTT代码(下文省略),以及习惯性模运算优化:

inline void NTT(int *e,int ptr,int len)//ptr=1 正变换 ptr=0 逆变换
{
    int i,j,k,ori,G,pd,ix,iy;G= ptr? g:inv[g];//g:原根 inv[g]:g的逆元 
    for(i=1;i<len;++i) if(i<rv[i]) swap(e[i],e[rv[i]]);
    for(i=1;i<len;i<<=1){
        ori=fp(G,(mod-1)/(i<<1));
        for(j=0;j<len;j+=(i<<1)){
            pd=1;
            for(k=0;k<i;++k,pd=mul(pd,ori)){
                ix=e[j+k];iy=mul(e[i+j+k],pd);
                e[j+k]=ad(ix,iy);e[i+j+k]=dc(ix,iy);
            }
        }
    }
    if(ptr) return;
    for(i=0;i<len;++i) e[i]=mul(e[i],inv[len]);
}
inline int ad(int x,int y) {x+=y;if(x>=mod) x-=mod;return x;}
inline int dc(int x,int y) {x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y) {return 1ll*x*y%mod;}

牛顿迭代

解近似方程根 h ( x ) = 0 h(x)=0 h(x)=0的一种方法。

h ( x ) h(x) h(x)当前近似解为 x n x_n xn,进一步迭代:

x n + 1 = x n − h ( x n ) h ′ ( x n ) x n x_{n+1}=x_n-\dfrac {h(x_n)}{\frac {h'(x_n)}{x_n}} xn+1=xnxnh(xn)h(xn)

引申:当 h ( x ) h(x) h(x)中嵌套一个 f ( x ) f(x) f(x)构成复合函数 h ( f ( x ) ) = 0 h(f(x))=0 h(f(x))=0,同样可以牛顿迭代倍增求解近似的 f ( x ) f(x) f(x),满足 g ( x ) ≡ h ( f n ( x ) ) ≡ 0 ( m o d   x 2 n ) g(x)\equiv h(f_n(x))\equiv 0(mod \ x^{2^n}) g(x)h(fn(x))0(mod x2n)

f n + 1 ( x ) ≡ f n ( x ) − g ( x ) g ′ ( x ) ( m o d   x 2 n + 1 ) f_{n+1}(x)\equiv f_n(x)-\dfrac {g(x)}{ {g'(x)}{}}(mod\ x^{2^{n+1}}) fn+1(x)fn(x)g(x)g(x)(mod x2n+1)

注: g ′ ( x ) g'(x) g(x) g g g h h h的偏导, g ′ ( x ) = h ′ ( f n ( x ) ) g'(x)=h'(f_n(x)) g(x)=h(fn(x))

上式可以用泰勒展开证明当 f n ( x ) f_n(x) fn(x) x 2 n x^{2^n} x2n项满足条件, f n + 1 ( x ) f_{n+1}(x) fn+1(x) x 2 n + 1 x^{2^{n+1}} x2n+1项满足条件。

考虑 g ( x ) g(x) g(x) f n x f_n{x} fnx处的泰勒展开式:

g ( x ) = ∑ i = 0 ∞ ∂ i h ( x ) ( ∂ x ) i ( f n ( x ) ) i ! ( f ( x ) − f n ( x ) ) i g(x)=\sum_{i=0} ^{\infty} \dfrac{\frac {\partial ^i h(x)}{(\partial x)^i}(f_n(x)) }{i!}(f(x)-f_n(x))^i g(x)=i=0i!(x)iih(x)(fn(x))(f(x)fn(x))i

这里令 0 ! = 1 0!=1 0!=1

因为 f n ( x ) f_n(x) fn(x)存在前 x 2 n x^{2^n} x2n项满足条件,当 i ≥ 2 i\geq 2 i2 ( f ( x ) − f n ( x ) ) (f(x)-f_n(x)) (f(x)fn(x))最小次幂不小于 x 2 n + 1 x^{2^{n+1}} x2n+1次方。

所以牛顿迭代求得就是泰勒展开式的前两项:

g ( x ) ≡ h ( f n ( x ) ) + h ′ ( f n ( x ) ) ( f n + 1 ( x ) − f n ( x ) ) ( m o d   x 2 n + 1 ) g(x)\equiv h(f_{n}(x))+h'(f_{n}(x))(f_{n+1}(x)-f_n(x))(mod\ x^{2^{n+1}}) g(x)h(fn(x))+h(fn(x))(fn+1(x)fn(x))(mod x2n+1)

由因为目标函数 g ( x ) = 0 g(x)=0 g(x)=0,故:

f n + 1 ( x ) = f n x − g ( x ) g ′ ( x ) f_{n+1}(x)=f_n{x}-\dfrac{g(x)}{{g'(x)}{}} fn+1(x)=fnxg(x)g(x)


基本运算&初等函数


求逆

给定 f ( x ) f(x) f(x),求 g ( x ) g(x) g(x)满足 f ( x ) ∗ g ( x ) ≡ 1 ( m o d   x n ) f(x)*g(x)\equiv 1(mod\ x^n) f(x)g(x)1(mod xn)

倍增(递归分治)求解。

法1:

观察 f ( x ) ∗ g n ( x ) − 1 ≡ 0 ( m o d   x 2 n ) f(x)*g_n(x)-1\equiv 0(mod\ x^{2^n}) f(x)gn(x)10(mod x2n)

平方后得:

f 2 ( x ) ∗ g n 2 ( x ) − 2 ∗ f ( x ) ∗ g n ( x ) + 1 ≡ 0 ( m o d   x 2 n + 1 ) f^2(x)*g_n^2(x)-2*f(x)*g_n(x)+1\equiv 0(mod\ x^{2^{n+1}}) f2(x)gn2(x)2f(x)gn(x)+10(mod x2n+1)

f ( x ) ∗ g n + 1 ( x ) ≡ 1 ( m o d   x 2 n + 1 ) f(x)*g_{n+1}(x)\equiv 1(mod\ x^{2^{n+1}}) f(x)gn+1(x)1(mod x2n+1)代入:

f ( x ) ∗ g n + 1 ( x ) ≡ 2 ∗ f ( x ) ∗ g n ( x ) − f 2 ( x ) ∗ g n 2 ( x ) ( m o d   x 2 n + 1 ) f(x)*g_{n+1}(x)\equiv 2*f(x)*g_n(x)-f^2(x)*g_n^2(x)(mod \ x^{2^{n+1}}) f(x)gn+1(x)2f(x)gn(x)f2(x)gn2(x)(mod x2n+1)

最终得到:

g n + 1 ( x ) ≡ 2 ∗ g n ( x ) − f ( x ) ∗ g n 2 ( x ) ( m o d   x 2 n + 1 ) g_{n+1}(x)\equiv 2*g_n(x)-f(x)*g_n ^2(x)(mod \ x^{2^{n+1}}) gn+1(x)2gn(x)f(x)gn2(x)(mod x2n+1)

法2:

考虑牛顿迭代求解:

底层为 g [ 0 ] = i n v ( f [ 0 ] ) g[0]=inv(f[0]) g[0]=inv(f[0])

h ( g ( x ) ) = f ( x ) ∗ g ( x ) − 1 = 0 h(g(x))=f(x)*g(x)-1=0 h(g(x))=f(x)g(x)1=0

g n + 1 ( x ) ≡ g n ( x ) − f ( x ) ∗ g n ( x ) − 1 f ( x ) ( m o d   x 2 n + 1 ) g_{n+1}(x)\equiv g_n(x)-\dfrac{f(x)*g_n(x)-1}{f(x)}(mod\ x^{2^{n+1}}) gn+1(x)gn(x)f(x)f(x)gn(x)1(mod x2n+1)

其中 h ′ ( g n ( x ) ) = 1 f ( x ) ≡ g n ( x ) ( m o d   x 2 n + 1 ) h'(g_n(x))= \frac{1}{f(x)}\equiv g_n(x)(mod\ x^{2^{n+1}}) h(gn(x))=f(x)1gn(x)(mod x2n+1)

g n + 1 ( x ) ≡ g n ( x ) − ( f ( x ) ∗ g n ( x ) − 1 ) ∗ g n ( x ) ( m o d   x 2 n + 1 ) g_{n+1}(x)\equiv g_n(x)-(f(x)*g_n(x)-1)*g_n(x)(mod\ x^{2^{n+1}}) gn+1(x)gn(x)(f(x)gn(x)1)gn(x)(mod x2n+1)

化简得到法1中的式子。

代码:

inline void getinverse(int n,int *f,int *g)
{
    if(n==1){g[0]=fastpow(f[0],mod-2);return;}
    getinv((n+1)>>1,f,g);
    int i,j,len=1,L=0;
    static int cont[N];
    for(;len<n+n;len<<=1) L++;
    for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
    for(i=0;i<n;++i) cont[i]=f[i];
    for(i=n;i<len;++i) cont[i]=0;
    NTT(cont,1,len);NTT(g,1,len);
    for(i=0;i<len;++i) g[i]=mul(g[i],dc(2,mul(cont[i],g[i])));
    NTT(g,0,len);
    for(i=n;i<len;++i) g[i]=0;
}

开根

给定 f ( x ) f(x) f(x),求 g ( x ) g(x) g(x)满足 f ( x ) ≡ g 2 ( x ) ( m o d   x n ) f(x)\equiv g^2(x)(mod\ x^n) f(x)g2(x)(mod xn)

倍增(递归分治)求解。

法1:

观察 g n 2 ( x ) − f ( x ) ≡ 0 ( m o d   x 2 n ) g_n^2(x)-f(x)\equiv 0(mod \ x^{2^n}) gn2(x)f(x)0(mod x2n)

平方后得:

g n 4 ( x ) − 2 ∗ g n 2 ( x ) ∗ f ( x ) + f 2 ( x ) ≡ 0 ( m o d   x 2 n + 1 ) g_n^4(x)-2*g_n^2(x)*f(x)+f^2(x)\equiv 0 (mod \ x^{2^{n+1}}) gn4(x)2gn2(x)f(x)+f2(x)0(mod x2n+1)

巧妙地移项:

( g n 2 ( x ) + f ( x ) ) 2 ≡ 4 ∗ g n 2 ( x ) ∗ f ( x ) (g_n^2(x)+f(x))^2 \equiv 4*g_n^2(x)*f(x) (gn2(x)+f(x))24gn2(x)f(x)

化归得:

( g n 2 ( x ) + f ( x ) 2 ∗ g n 2 ( x ) ) 2 ≡ f ( x ) ( m o d   x 2 n + 1 ) (\dfrac{g_n^2(x)+f(x)}{2*g_n^2(x)})^2 \equiv f(x)(mod\ x^{2^{n+1}}) (2gn2(x)gn2(x)+f(x))2f(x)(mod x2n+1)

f ( x ) ≡ g n + 1 2 ( x ) ( m o d   x 2 n + 1 ) f(x)\equiv g_{n+1}^2(x)(mod\ x^{2^{n+1}}) f(x)gn+12(x)(mod x2n+1),故:

g n + 1 ( x ) ≡ g n 2 ( x ) + f ( x ) 2 ∗ g n 2 ( x ) ( m o d   x 2 n + 1 ) g_{n+1}(x) \equiv \dfrac{g_n^2(x)+f(x)}{2*g_n^2(x)}(mod\ x^{2^{n+1}}) gn+1(x)2gn2(x)gn2(x)+f(x)(mod x2n+1)

法2:

考虑牛顿迭代求解:

底层为 g [ 0 ] = s q r t ( f [ 0 ] ) g[0]=sqrt(f[0]) g[0]=sqrt(f[0])

h ( g ( x ) ) = g 2 ( x ) − f ( x ) = 0 h(g(x))=g^2(x)-f(x)=0 h(g(x))=g2(x)f(x)=0

g n + 1 ( x ) ≡ g n ( x ) − g n 2 ( x ) − f ( x ) 2 ∗ g n ( x ) ( m o d   x 2 n + 1 ) g_{n+1}(x)\equiv g_n(x)-\dfrac{g_n ^2 (x)-f(x)}{2*g_n(x)}(mod \ x^{2^{n+1}}) gn+1(x)gn(x)2gn(x)gn2(x)f(x)(mod x2n+1)

化简得到法1中的式子。

代码:

inline void getsqrt(int n,int *f,int *g)
{
    if(n==1) {g[0]=sqrt(f[0]);return;}
    getsqrt((n+1)>>1,f,g);
    int i,j,L=0,len=1;
    for(;len<n+n;len<<=1) L++;
    static int inv[N],tp[N];
    for(i=0;i<len;++i) inv[i]=0;
    getinv(n,g,inv);
    for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|((i&1)<<(L-1)));
    for(i=0;i<n;++i) tp[i]=f[i];
    for(i=n;i<len;++i) tp[i]=0;
    NTT(inv,1,len);NTT(g,1,len); NTT(tp,1,len);
    for(i=0;i<len;++i) g[i]=mul(ad(mul(g[i],g[i]),tp[i]),mul(iv[2],inv[i]));
    NTT(g,0,len);
    for(i=n;i<len;++i) g[i]=0;
}

ln

给定 f ( x ) f(x) f(x),求 g ( x ) ≡ l n ( f ( x ) ) ( m o d   x n ) g(x)\equiv ln(f(x)) (mod \ x^n) g(x)ln(f(x))(mod xn)

l n ln ln不能用牛顿迭代求解,因为 h ( g ( x ) ) h(g(x)) h(g(x)) g ( x ) g(x) g(x)的系数为1,迭代会产生恒等式。

对等式两侧求导:

g ′ ( x ) ≡ f ′ ( x ) f ( x ) g'(x)\equiv \frac {f'(x)}{f(x)} g(x)f(x)f(x)

g ( x ) ≡ ∫ f ′ ( x ) f ( x ) d x + C ( m o d   x n ) g(x)\equiv \int \frac {f'(x)}{f(x)} dx +C(mod \ x^n) g(x)f(x)f(x)dx+C(mod xn)

注意求积分 C = 0 C=0 C=0后默认 f [ 0 ] = 1 f[0]=1 f[0]=1,所以 f [ 0 ] ≠ 1 f[0]\neq 1 f[0]̸=1时要特殊处理 C C C(代入任意值计算处理)。

代码:

inline void getln(int n,int *f,int *g)
{
    int i,j,len=1,L=0;
    for(;len<n+n;len<<=1) L++;
    static int der[N],nv[N];
    for(i=0;i<len;++i) der[i]=nv[i]=0;
    getderivative(n,f,der);
    getinverse(n,f,nv);
    for(i=1;i<len;++i) rv[i]=((rv[i>>1]>>1)|(i&1)<<(L-1));
    NTT(nv,1,len);NTT(der,1,len);
    for(i=0;i<len;++i) der[i]=mul(der[i],nv[i]);
    NTT(der,0,len);
    getintegration(n,der,g);
}

exp

给定 f ( x ) f(x) f(x),求 g ( x ) g(x) g(x)满足 e f ( x ) ≡ g ( x ) ( m o d   x n ) e^{f(x)}\equiv g(x) (mod \ x^n) ef(x)g(x)(mod xn)

倍增(递归分治)求解。求exp只能用牛顿迭代了。

考虑取 l n ln ln变形:

l n ( g ( x ) ) ≡ f ( x ) ( m o d   x n ) ​ ln(g(x))\equiv f(x) (mod\ x^n)​ ln(g(x))f(x)(mod xn)

底层为 g [ 0 ] = e f [ 0 ] g[0]=e^{f[0]} g[0]=ef[0](一般情况下默认 f ( x ) f(x) f(x)常数项为0)。

h ( g ( x ) ) = l n ( g ( x ) ) − f ( x ) = 0 h(g(x))=ln(g(x))-{f(x)}=0 h(g(x))=ln(g(x))f(x)=0

g n + 1 ( x ) ≡ g n x − l n ( g n ( x ) ) − f ( x ) 1 g n ( x ) ( m o d   x 2 n + 1 ) ) g_{n+1}(x)\equiv g_n{x} - \dfrac {ln(g_n(x))-f(x)}{\frac{1}{g_n(x)}}(mod \ x^{2^{n+1}})) gn+1(x)gnxgn(x)1ln(gn(x))f(x)(mod x2n+1))

g n + 1 ( x ) ≡ g n ( x ) ∗ ( 1 − l n ( g n ( x ) ) + f ( x ) ) ( m o d   x 2 n + 1 ) g_{n+1}(x)\equiv g_n(x)*(1-ln(g_n(x))+f(x))(mod \ x^{2^{n+1}}) gn+1(x)gn(x)(1ln(gn(x))+f(x))(mod x2n+1)

代码:

inline void getexp(int n,int *f,int *g)
{
    if(n==1) {g[0]=1;return;}
    getexp((n+1)>>1,f,g);
    int i,j,L=0,len=1;
    for(;len<n+n;len<<=1) L++;
    static int ln[N];
    for(i=0;i<len;++i) ln[i]=0;
    getln(n,g,ln); 
    for(i=0;i<n;++i) ln[i]=dc(f[i],ln[i]);
    ln[0]=ad(ln[0],1);
    NTT(ln,1,len);NTT(g,1,len);
    for(i=0;i<len;++i) g[i]=mul(g[i],ln[i]); 
    NTT(g,0,len);
    for(i=n;i<len;++i) g[i]=0;
}

快速幂

给定 f ( x ) , k f(x),k f(x),k,求 g ( x ) g(x) g(x)满足 g ( x ) ≡ f k ( x ) ( m o d   x n ) g(x)\equiv f^k(x)(mod\ x^n) g(x)fk(x)(mod xn)

考虑取 l n ln ln变形,再次进行降幂:

l n ( g ( x ) ) ≡ k ∗ l n ( f ( x ) ) ( m o d   x n ) ln(g(x))\equiv k*ln(f(x))(mod\ x^n) ln(g(x))kln(f(x))(mod xn)

故:

g ( x ) ≡ e k ∗ l n ( f ( x ) ) ( m o d   x n ) g(x)\equiv e^{k*ln(f(x))}(mod\ x^n) g(x)ekln(f(x))(mod xn)

代码:

inline void getpow(int n,int *f,int *g,int K)
{
    int i,j,L=0,len=1;
    getln(n,f,g);
    for(;len<n;len<<=1) L++;
    for(i=0;i<len;++i) g[i]=mul(g[i],K);
    for(i=0;i<n;++i) f[i]=0;
    getexp(n,g,f);
}

三角函数

给定 P ( x ) P(x) P(x),求 X ( x ) , Y ( x ) X(x),Y(x) X(x),Y(x)满足

cos ⁡ P ( x ) − X ( x ) ≡ 0 (   m o d   x n ) \cos P(x) - X(x) \equiv 0 (\bmod{x^n}) cosP(x)X(x)0(modxn)

sin ⁡ P ( x ) − Y ( x ) ≡ 0 (   m o d   x n ) \sin P(x) - Y(x) \equiv 0 (\bmod{x^n}) sinP(x)Y(x)0(modxn)

由欧拉公式:

e i P ( x ) = c o s P ( x ) + i s i n P ( x ) e^{iP(x)}=cosP(x)+isinP(x) eiP(x)=cosP(x)+isinP(x)

所以可以用复数二元组,实部表示 X ( x ) X(x) X(x),虚部表示 Y ( x ) Y(x) Y(x)

X ( x ) + i Y ( x ) = e i P ( x ) X(x)+iY(x)=e^{iP(x)} X(x)+iY(x)=eiP(x)

求一下 e x p exp exp分别对应。

貌似可以 F F T FFT FFT直接算,但应用程度不高。

所以是理论可行。代码?唔,不存在的(留坑)。


除法&取模

多项式除法与取模


形式幂级数&拉格朗日反演

给定 f ( x ) , k f(x),k f(x),k,求 g ( x ) g(x) g(x) k k k次项系数 g [ k ] g[k] g[k],满足 f ( g ( x ) ) = x ( m o d   x n ) f(g(x))=x(mod\ x^n) f(g(x))=x(mod xn)

形式幂级数:

对于一个多项式,仅有有限项的系数是非零的。而对于形式幂级数,则去掉这一限制,将项数与系数推广到无穷大:

F ( x ) = ∑ i = 0 ∞ a i x i F(x)=\sum _{i=0} ^{\infty}a_ix^i F(x)=i=0aixi

其中系数 a i a_i ai均为环 R [ x ] R[x] R[x](可以是实数/复数/整数/模意义下剩余类)中的元素,为方便表示,设 [ x n ] [x^n] [xn]表示 F ( x ) F(x) F(x) x n x^n xn的系数。所有的形式幂级数构成形式幂级数环 R [ [ x ] ] R[[x]] R[[x]]

拉格朗日反演:

F ( x ) , G ( x ) ∈ R [ [ x ] ] F(x),G(x)\in R[[x]] F(x),G(x)R[[x]] F ( G ( x ) ) = x F(G(x))=x F(G(x))=x,则

[ x n ] G ( x ) = 1 n [ x − 1 ] 1 F n ( x ) [x^n]G(x)=\dfrac 1n[x^{-1}]\dfrac {1}{F^n(x)} [xn]G(x)=n1[x1]Fn(x)1

F ( x ) F(x) F(x) G ( x ) G(x) G(x)的复合逆。

特别的,若 F ( x ) = x ϕ ( x ) F(x)=\dfrac x{\phi (x)} F(x)=ϕ(x)x,则:

[ x n ] G ( x ) = 1 n [ x n − 1 ] ϕ n ( x ) [x^n]G(x)=\dfrac1n[x^{n-1}]\phi^n(x) [xn]G(x)=n1[xn1]ϕn(x)

F ( x ) = x ϕ ( x ) F(x)=\dfrac x{\phi (x)} F(x)=ϕ(x)x,得 ϕ ( x ) = x F ( x ) \phi (x)=\dfrac x{F(x)} ϕ(x)=F(x)x,代入上式:

[ x n ] G ( x ) = 1 n [ x n − 1 ] ( x F ( x ) ) n [x^n]G(x)=\dfrac1n[x^{n-1}]\left(\dfrac x{F(x)}\right)^n [xn]G(x)=n1[xn1](F(x)x)n

进一步拓展:

[ x n ] h ( g ( x ) ) = 1 n [ x n − 1 ] h ′ ( x ) ( x f ( x ) ) n [x^n]h(g(x))=\dfrac{1}{n}[x^{n-1}]h'(x)\left(\dfrac{x}{f(x)}\right)^n [xn]h(g(x))=n1[xn1]h(x)(f(x)x)n(三层嵌套)

证明详见拉格朗日反演- hjuy134

代码:

inline int lagrange(int n,int *F,int *G,int k)
{
	static int res[N];
	getinverse(n,F,res);
	for(i=n-1;i>=1;--i) res[i]=res[i-1];
	res[0]=0; 
	getpow(n,res,G,k);
	return mul(G[k-1],fastpow(k,mod-2)); 
}

时间复杂度总结

原文戳这里

内容 时间复杂度 常数
卷积 O ( n l o g n ) O(nlogn) O(nlogn) 3
求逆 O ( n l o g n ) O(nlogn) O(nlogn) 6
开根 O ( n l o g n ) O(nlogn) O(nlogn) 18
求导 O ( n ) O(n) O(n) 1
积分 O ( n ) O(n) O(n) 1
ln O ( n l o g n ) O(nlogn) O(nlogn) 9
exp O ( n l o g n ) O(nlogn) O(nlogn) 24
快速幂 O ( n l o g n ) O(nlogn) O(nlogn) 33

求值&插值


多点求值

给定 f ( x ) f(x) f(x)以及点集 X = { x 1 , x 2 , . . . , x n } X=\{x_1,x_2,...,x_n \} X={x1,x2,...,xn},求 f ( x 1 ) , f ( x 2 ) , . . . , f ( x n ) f(x_1),f(x_2),...,f(x_n) f(x1),f(x2),...,f(xn)

倍增(递归分治)求解。

考虑底层:由拉格朗日插值法式,得到 F ( x i ) = F ( x ) ( m o d   ( x − x i ) ) ( x i ) F(x_i)=F(x)(mod\ (x-x_i))(x_i) F(xi)=F(x)(mod (xxi))(xi)

观察到 ∃ f ′ ( x ) = f ( x ) m o d ( ∏ i = 1 n ( x − x i ) ) \exists f'(x)=f(x) mod (\prod _{i=1} ^n (x-x_i)) f(x)=f(x)mod(i=1n(xxi)),满足 f ′ ( x ) = f ( x ) ( 1 ≤ i ≤ n ) f'(x)=f(x) (1\leq i\leq n) f(x)=f(x)(1in)

因为 f ( x ) f(x) f(x)在可取范围 d e g ( f ( x ) ) ≥ n − 1 deg(f(x))\geq n-1 deg(f(x))n1内的答案都是唯一的,所以可以保留到这 n n n个点的

最简拉格朗日插值式化出的多项式( d e g ( f ) = n − 1 deg(f)=n-1 deg(f)=n1),而不会影响答案,而这样的一个式子必然仅

存在于 ∏ i = 1 n ( x − x i ) \prod _{i=1} ^n (x-x_i) i=1n(xxi)(最高项次幂为 n > d e g ( f ) n>deg(f) n>deg(f))的剩余类中,故等式成立。

那么考虑分治处理,设:

X 0 = { x 0 , x 1 , . . . , x ⌊ n 2 ⌋ } X_0=\{x_0,x_1,...,x_{\lfloor{\frac n2} \rfloor} \} X0={x0,x1,...,x2n} X 1 = { x ⌊ n 2 ⌋ + 1 , x ⌊ n 2 ⌋ + 2 , . . . , x n } X_1=\{x_{\lfloor {\frac n2 }\rfloor+1},x_{\lfloor {\frac n2}\rfloor+2},...,x_n\} X1={x2n+1,x2n+2,...,xn}

g 0 ( x ) = ∏ i = 1 ⌊ n 2 ⌋ ( x − x i ) g_0(x)=\prod _{i=1} ^{\lfloor{\frac n2} \rfloor} (x-x_i) g0(x)=i=12n(xxi) g 1 ( x ) = ∏ i = ⌊ n 2 ⌋ + 1 n ( x − x i ) g_1(x)=\prod _{i={\lfloor {\frac n2}\rfloor}+1}^n (x-x_i) g1(x)=i=2n+1n(xxi)

显然对于 x i ∈ X 0 x_i\in X_0 xiX0 g 0 ( x i ) = 0 g_0(x_i)=0 g0(xi)=0;对于 x i ∈ X 1 x_i\in X_1 xiX1 g 1 ( x i ) = 0 g_1(x_i)=0 g1(xi)=0

于是每次二分递归,将两边连乘积合并即可,先预处理出所有递归中取模后的多项式,再递归处理。

靠谱的做法是小于一定数量时暴力算,因为多点求值的常数太大了(复杂度 O ( n l o o o o o o g 2 n ) O(nloooooog^2n) O(nloooooog2n))。

代码:

#define mid (((l)+(r))>>1)
inline void prework(int dep,int l,int r,int *x,int *consmul)//预处理连乘积
{
	if(l==r){
		consmul[dep][l<<1]=mod-x[l];
		consmul[dep][l<<1|1]=1;//递归底部:x-xi
		return;
	}
	prework(dep+1,l,mid,x,consmul);
	prework(dep+1,mid+1,r,x,consmul);
	getmul(consmul[dep+1]+(l<<1),mid-l+1,consmul[dep+1]+((mid+1)<<1),r-mid,consmul[dep]+(l<<1),r-l+1);
}

inline void getval(int dep,int l,int r,int *f,int *y,int *consmul)
{
	static int modulo[D][N];//记录取模后多项式
	getmod(f,r-l,consmul[dep]+(l<<1),r-l+1,modulo[dep]+l);
	if(l==r){y[l]=modulo[dep][l];return;}
	getval(dep+1,l,mid,modulo[dep]+l,y,consmul);
	getval(dep+1,mid+1,r,modulo[dep]+mid+1,y,consmul);
}

inline void getval(int *f,int *x,int n,int *y)
{
	static int consmul[D][N<<1];//记录连乘积
	prework(0,1,n,x,dc);
	getval(0,1,n,f,y,dc);
}

注:此代码没有提交过,不保证正确性。


快速插值(牛顿插值&拉格朗日优化)

给定二元组集合 P = { ( x 1 , f ( x 1 ) ) , ( x 2 , f ( x 2 ) , . . . , ( x n , f ( x n ) ) } P=\{(x_1,f(x_1)),(x_2,f(x_2),...,(x_n,f(x_n)) \} P={(x1,f(x1)),(x2,f(x2),...,(xn,f(xn))},求 f ( x ) ( d e g ( f ) < n ) f(x)(deg(f)<n) f(x)(deg(f)<n)

首先介绍一种不快速但很好手算的方法,牛顿插值(增量法):

g n ( x ) = ∏ i = 1 n ( x − x i ) g_n(x)=\prod _{i=1}^n(x-x_i) gn(x)=i=1n(xxi)

设前 n n n个点插值得到 f n ( x ) f_n(x) fn(x),考虑 f n + 1 ( x ) f_{n+1}(x) fn+1(x):

f n + 1 ( x ) ≡ f n ( x ) ( m o d   g ( n ) ) f_{n+1}(x)\equiv f_n(x) (mod\ g(n)) fn+1(x)fn(x)(mod g(n))

f n + 1 ( x ) = f n ( x ) + k ∗ g ( n ) f_{n+1}(x)=f_n(x)+k*g(n) fn+1(x)=fn(x)+kg(n)

代入 x = x n + 1 x=x_{n+1} x=xn+1,解出 k k k,即可求出 f n + 1 ( x ) f_{n+1}(x) fn+1(x)。复杂度 O ( n 2 ) O(n^2) O(n2)

现在来考虑一种复杂度 O ( n l o o o o o o o o o g 2 n ) O(nlooooooooog^2n) O(nlooooooooog2n)快速插值:

观察拉格朗日插值式:

f ( x ) = ∑ i = 1 n f ( x i ) ∏ j = 1 , j ≠ i n x − x j x i − x j f(x)=\sum_ {i=1} ^n f(x_i) \prod _{j=1,j\neq i}^{n} \dfrac {x-x_j}{x_i-x_j} f(x)=i=1nf(xi)j=1,j̸=inxixjxxj

通过化简优化一下这个 O ( n 2 ) O(n^2) O(n2)的式子:

f ( x ) = ∑ i = 1 n f ( x i ) ∏ j = 1 , j ≠ i n ( x i − x j ) ∏ j = 1 , j ≠ i n ( x − x j ) f(x)=\sum_{i=1} ^n \dfrac {f(x_i)}{\prod _{j=1,j\neq i}^n (x_i-x_j)}\prod _{j=1,j\neq i}^n(x-x_j) f(x)=i=1nj=1,j̸=in(xixj)f(xi)j=1,j̸=in(xxj)

g ( x ) = ∏ i = 1 n ( x − x i ) ​ g(x)=\prod _{i=1} ^n (x-x_i)​ g(x)=i=1n(xxi) g i ( x ) = g ( x ) x − x i g_i(x)=\frac {g(x)}{x-x_i} gi(x)=xxig(x)

显然 ∏ j = 1 , j ≠ i n ( x i − x j ) = g i ( x ) \prod _{j=1,j\neq i}^n (x_i-x_j)=g_i(x) j=1,j̸=in(xixj)=gi(x)

g ( x ) = ( x − x i ) g i ( x ) g(x)=(x-x_i)g_i(x) g(x)=(xxi)gi(x)巧妙地求导(利用洛必达法则)后发现:

g ′ ( x ) = g i ( x ) + ( x − x i ) g ′ ( x ) g'(x)=g_i(x)+(x-x_i)g'(x) g(x)=gi(x)+(xxi)g(x)

故: g i ( x i ) = g ′ ( x i ) g_i(x_i)=g'(x_i) gi(xi)=g(xi)

g i ( x i ) g_i(x_i) gi(xi)变成了 g ′ ( x ) g'(x) g(x)的多点求值。

那么 1 ∏ j = 1 , j ≠ i n ( x i − x j ) \frac {1}{\prod _{j=1,j\neq i}^n(x_i-x_j)} j=1,j̸=in(xixj)1就可以快速求出了,而 ∏ j = 1 , j ≠ i n ( x − x j ) = ( x − x i ) ∏ j = 1 n ( x − x j ) \prod _{j=1,j\neq i}^n(x-x_j)=(x-x_i)\prod_{j=1}^n(x-x_j) j=1,j̸=in(xxj)=(xxi)j=1n(xxj),直接用分治 F F T FFT FFT求出连乘积(类似多点求值中的预处理)即可。最后 O ( n ) O(n) O(n)合并答案。

代码:

#define mid (((l)+(r))>>1)
inline void getinterpolation(int dep,int l,int r,int *val,int *consmul,int *f)
{
	if(l==r) {f[l]=val[l];return;}
	getinterpolation(dep+1,l,mid,val,consmul,f);
	getinterpolation(dep+1,mid+1,r,val,consmul,f);
	static int g[N],h[N];
	getmul(f+l,mid-l,consmul[dep+1]+((mid+1)<<1),r-mid,g,r-l);
	getmul(f+mid+1,r-mid-1,consmul(dep+1)+(l<<1),l-mid+1,h,r-l);
    //类似线段树与另一子节点合并过程,每一位乘上除自己位以外的所有连乘积
    //注意总次数是r-l,不是r-l+1,本区间向乘上另一区间时次数会少一位(少乘了自己)
	getadd(g,h,f+l,r-l);//合并
}

inline void getinterpolation(int *x,int *y,int n,int *f)
{
	static int consmul[D][N<<1],res[N],val[N];
	prework(0,1,n,x,consmul);//预处理连乘积
	getderivative(n,consmul[0],res);//处理g'(x)
	getval(res,x,n,val);//多点求值得到gi(xi)
	for(int i=1;i<=n;++i)
	 val[i]=mul(y[i],inv[val[i]]);
	getinterpolation(0,1,n,val,consmul,f);//最后乘上式子右边一部分
}

注:此代码没有提交过,不保证正确性。


参考资料

  • FFT 学习笔记-menci
  • 有关多项式处理的各种算法总结-zlttttt
  • 多项式求ln,求exp,开方,快速幂 学习总结-_Vertical
  • 多项式的运算-KirinBill
  • [拉格朗日反演][FFT][NTT][多项式大全]详解-TJY
  • Pick’s Blog
  • Miskcoo’s Space
  • 2015国家集训队论文 鏼 生成函数的运算与组合计数问题
  • 2016国家集训队论文 myy 再探快速傅里叶变换
  • Foolmike的多项式课件

膜膜膜众神犇。也非常感谢能有这么多这么好的资源。

如果有错误欢迎指出,有疑问欢迎评论。

你可能感兴趣的:(---多项式---)