多項式學習筆記

在最前面講一些廢話

Q:你不是都退役了嗎,爲何還更博客?git

A:文化課太無聊了,我來更新更新。github

Q:不是說不想學多項式了嗎,爲何仍是學了?函數

A:下一週有浙大的校賽,而後發現咱們隊裏沒有人會多項式,他們都去吃雞了,因而就(把鍋甩給我了)去學了。ui

Q:爲何是準備浙大校賽,而不是準備ZJOIday2。spa

A:由於我day1被出題人卡常數送退役了,day2也沒啥意義,並且出多項式我也必定拿不了什麼分。code

Q:那你這麼菜這篇文章有什麼好看的。get

A:由於我數學和記性不太好,寫下來幫助本身理解用的,想學多項式左轉 txc好哥哥的博客博客


多項式乘法

都9102年了還有人不會 \(\text{FFT/NTT}\) 嗎,想必任意模數也沒啥用,就省略好了。數學

算了,仍是貼一個跑的很是慢的代碼吧。it

code

inline void timesinit(int lenth){
    for(len = 1, lg = 0; len <= lenth; len <<= 1, lg++);
    for(int i = 0; i < len; i++)
       rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1));
}
inline void DFT(int *a, int sgn){
   for(int i = 0; i < len; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
   for(int k = 2; k <= len; k <<= 1){
       int w = ~sgn ? Pow(G, (P - 1) / k) : Pow(Pow(G, (P - 1) / k), P - 2);
       for(int i = 0; i < len; i += k){
           int now = 1;
           for(int j = i; j < i + (k >> 1); j++, now = 1ll * now * w % P){
               int x = a[j], y = 1ll * a[j+(k>>1)] * now % P;
               a[j] = (x + y) % P, a[j+(k>>1)] = (x - y + P) % P;
           }
      }
  }
  if(sgn == -1){
      int Inv = Pow(len, P - 2);
      for(int i = 0; i < len; i++) a[i] = 1ll * a[i] * Inv % P;
  }
}
inline void times(int *a, int *b, int *c){
    for(int i = 0; i < len; i++) c[i] = 1ll * a[i] * b[i] % P;
}


多項式求逆

如今有多項式 \(F(x)\) ,求一個多項式 \(G(x)\) ,知足 \(F(x)G(x)\equiv 1\)

考慮倍增,假設當前要求
\[ F(x)G(x)\equiv1(\bmod x^n) \]
而且已知
\[ F(x)G_0(x)\equiv1(\bmod x^\frac{n}{2}) \]
兩式相減能夠獲得
\[ F(x)(G(x)-G_0(x))\equiv0(\bmod x^\frac{n}{2}) \\ G(x)-G_0(x)\equiv0(\bmod x^\frac{n}{2}) \\ \]
兩邊都平方能夠獲得
\[ G^2(x)-2G(x)G_0(x)+G_0^2(x)\equiv 0(\bmod x^n) \]
乘上 \(F(x)\) 獲得
\[ G(x)-2G_0(x)+G_0^2(x)F(x)\equiv0(\bmod x^n) \\ G(x)\equiv G_0(x)(2-G_0(x)F(x)) (\bmod x^n) \]
一次操做須要兩次 \(\text{DFT}\) ,複雜度 \(T(n)=T(\frac{n}{2})+\mathcal O(n\log n)=\mathcal O(n\log n)\)

code

inline void getinv(int *a, int *b, int n){
    if(n == 1) return (void) (b[0] = Pow(a[0], P - 2));
    getinv(a, b, (n + 1) / 2);
    static int tmp[N];
    timesinit(n * 2 - 1);
    for(int i = 0; i < len; i++) tmp[i] = i < n ? a[i] : 0;
    DFT(tmp, 1), DFT(b, 1);
    for(int i = 0; i < len; i++) 
        b[i] = 1ll * (2 - 1ll * tmp[i] * b[i] % P + P) % P * b[i] % P;
    DFT(b, -1);
    for(int i = n; i < len; i++) b[i] = 0;
    for(int i = 0; i < len; i++) tmp[i] = 0;
}


多項式牛頓迭代

和普通的牛頓迭代形式同樣,從多項式的角度推一下

咱們有關於多項式 \(F(x)\) 的方程 \(G(F(x))=0\),而且假設當前已經求出
\[ G(F_0(x))\equiv0\ (\bmod x^{n}) \]
在這基礎上咱們要求 \(G(F(x))\equiv 0(\bmod x^{2n})\)

\(G(F(x))\)\(F_0(x)\) 處泰勒展開,能夠獲得
\[ G(F(x))=\sum_{i}\dfrac{G^i(F_0(x))}{i!}(F(x)-F_0(x))^i \]
其中 \(G^i\) 表示 \(G\)\(i\) 階導函數。

因爲 \(F(x)-F_0(x)\) 的前 \(n\) 項係數都是 \(0\) ,當 \((F(x)-F_0(x))^i\) 的次數大於 \(1\) 時,所得多項式的係數都是 \(0\) ,由於卷積的時候每一次乘法都至少有一個係數爲 \(0\) 的項參與,因此能夠獲得
\[ G(F(x))\equiv G(F_0(x))+G'(F_0(x))(F(x)-F_0(x))\equiv 0\ (\bmod x^{2n}) \\ F(x)\equiv F_0(x)+\dfrac{G(F(x))-G(F_0(x))}{G'(F_0(x))} (\bmod x^{2n}) \]
由於 \(G(F(x))\equiv 0 (\bmod x^{2n})\),因此有
\[ F(x)\equiv F_0(x)-\dfrac{G(F_0(x))}{G'(F_0(x))} (\bmod x^{2n}) \]
能夠用來推不少東西,開根,exp,三角函數什麼的,好像不能用來推求逆,推出來的式子要用到求逆。


多項式開根

如今有一個多項式 \(F(x)\),要求多項式 \(G(x)\equiv \sqrt{F(x)}\ (\bmod x^m)\)


\[ G(x)^2 =F(x)\\G(x)^2-F(x)=0 \]
假設當前已經有 \(G_0(x)^2-F(x)\equiv0\ (\bmod x^n)\) ,要求 \[G(x)^2-F(x)\equiv0\ (\bmod x^{2n})\]

套用牛頓迭代
\[ G(x)\equiv G_0(x)-\dfrac{G_0(x)^2-F(x)}{2G_0(x)}\ (\bmod x^{2n}) \\ G(x)\equiv \dfrac{1}{2}\left( G_0(x)-\dfrac{F(x)}{G_0(x)}) \right) \]
須要保證 \(F(x)\) 的常數項有二次剩餘。

複雜度根據主定理 \(T(n)=T(\dfrac{n}{2})+\mathcal O(n\log n)=\mathcal O(n \log n)\) ,每次須要一遍乘法一遍求逆 。

code

inline void getsqrt(int *a, int *b, int n){
    static int tmp1[N], tmp2[N];
    if(n == 1) return (void) (b[0] = 1);
    getsqrt(a, b, (n + 1) / 2);
    for(int i = 0; i < n; i++) tmp1[i] = a[i];
    getinv(b, tmp2, n), timesinit(n * 2 - 1);
    DFT(tmp1, 1), DFT(tmp2, 1);
    for(int i = 0; i < len; i++) tmp1[i] = 1ll * tmp1[i] * tmp2[i] % P;
    DFT(tmp1, -1);
    for(int i = 0; i < len; i++) 
        b[i] = 1ll * (b[i] + tmp1[i]) % P * Pow(2, P - 2) % P; 
    for(int i = n; i < len; i++) b[i] = 0;
    for(int i = 0; i < len; i++) tmp1[i] = tmp2[i] = 0;
}


多項式求ln

如今有一個多項式 \(F(x)\) ,要求 \(\ln(F(x))\)
\[ \ln(F(x))=\int (\ln(F(x)))' \\ =\int (\ln'(F(x))\cdot F'(x))\\ =\int\dfrac{F'(x)}{F(x)} \]

多項式求逆+多項式求導+多項式積分便可

多項式求逆複雜度 \(\mathcal O(n\log n)\),求導和積分下面式子來就行了,都是其中較爲簡單的。
\[ F(x)=\sum_{i\geq0}A_ix^i \\ F'(x)=\sum_{i\geq0}iA_{i+1}x^i \]
因此總的複雜度也是 \(\mathcal O(n\log n)\),注意積分之後是 \(F(x)+c\) 的形式,要保證 \(F(x)\) 常數項爲 \(1\)

code

inline void getln(int *a, int *b, int n){
    static int tmp[N];
    getinv(a, b, n), timesinit(n * 2 - 1);
    for(int i = 1; i < n; i++) tmp[i-1] = 1ll * a[i] * i % P;
    for(int i = n - 1; i < len; i++) tmp[i] = 0;
    DFT(tmp, 1), DFT(b, 1);
    for(int i = 0; i < len; i++) b[i] = 1ll * tmp[i] * b[i] % P;
    DFT(b, -1);
    for(int i = len - 1; i; i--) b[i] = 1ll * b[i-1] * Pow(i, P - 2) % P;
    b[0] = 0;   
    for(int i = n; i < len; i++) b[i] = 0;
    for(int i = 0; i < len; i++) tmp[i] = 0;
}


多項式求exp

如今有一個多項式 \(F(x)\) ,要求 \(G(x)\equiv e^{F(x)}\ (\bmod x^m)\)

先取一下對數
\[ \ln(G(x))-F(x)=0 \]
假設當前已經有 \(\ln(G_0(x))-F(x)\equiv0\ (\bmod x^n)\) ,要求 \[\ln(G_0(x))-F(x)\equiv0\ (\bmod x^{2n})\]

直接套用牛頓迭代
\[ G(x)\equiv G_0(x)- G_0(x)(\ln(G_0(x))-F(x))\ (\bmod x^{2n})\\ G(x)\equiv G_0(x)(1-\ln(G_0(x))+F(x))\ (\bmod x^{2n}) \]
須要保證 \(F(x)\) 的常數項爲 \(0\) ,求出來的 \(G(x)\) 的常數項必定爲 \(1\)

複雜度根據主定理 \(T(n)=T(\dfrac{n}{2})+\mathcal O(n\log n)=\mathcal O(n \log n)\) ,每次須要一遍乘法一遍 \(\ln\)

code

inline void getexp(int *a, int *b, int n){
    static int tmp[N];
    if(n == 1) return (void) (b[0] = 1);
    getexp(a, b, (n + 1) / 2);
    getln(b, tmp, n), timesinit(n * 2 - 1);
    for(int i = 0; i < n; i++) tmp[i] = (!i - tmp[i] + a[i] + P) % P;
    DFT(tmp, 1), DFT(b, 1);
    for(int i = 0; i < len; i++) b[i] = 1ll * b[i] * tmp[i] % P;
    DFT(b, -1);
    for(int i = n; i < len; i++) b[i] = 0;
    for(int i = 0; i < len; i++) tmp[i] = 0;
}


多項式冪函數

如今有一個多項式 \(F(x)\) ,要求 \(G(x)\equiv F(x)^k (\bmod x^m)\)

若是 \(F(x)\) 的常數項爲 \(1\) ,那麼有
\[ F(x)^k=\exp(k\ln(F(x))) \]
不然須要把 \(F(x)\) 的常數項變成 \(1\) ,找到最低的不爲 \(0\) 的項的係數 \([x^p]\) ,先讓多項式除以 \([x^p]x^p\) 這個單項式,而後就變成了一個常數項爲 \(1\) 的多項式 \(F_0(x)\)

最後再把這個單項式乘回來,有
\[ F(x)=[x^p]^kx^{pk}F_0(x) \]
須要一遍 \(\ln\) 和一遍 \(\exp\) ,複雜度 \(\mathcal O(n\log n)\) ,須要判的細節略微有點多。

code

inline void power(int *a, int *b, int n, int m, ll k){
    static int tmp[N];
    for(int i = 0; i < m; i++) b[i] = 0;
    int fir = -1;
    for(int i = 0; i < n; i++) if(a[i]){ fir = i; break; }
    if(fir && k >= m) return;
    if(fir == -1 || 1ll * fir * k >= m) return;
    for(int i = fir; i < n; i++) b[i-fir] = a[i];
    for(int i = 0; i < n - fir; i++) 
        b[i] = 1ll * b[i] * Pow(a[fir], P - 2) % P;
    getln(b, tmp, m);
    for(int i = 0; i < m; i++) 
        b[i] = 1ll * tmp[i] * (k % P) % P, tmp[i] = 0;
    getexp(b, tmp, m);
    for(int i = m; i >= fir * k; i--) 
        b[i] = 1ll * tmp[i-fir*k] * Pow(a[fir], k % (P - 1)) % P;
    for(int i = 0; i < fir * k; i++) b[i] = 0;
    for(int i = 0; i < m; i++) tmp[i] = 0;
}
相關文章
相關標籤/搜索