1. 程式人生 > 實用技巧 >多項式不全家桶

多項式不全家桶

多項式不全家桶(持續更新)

裡面可能不會很全,因為太難的東西我也不會啊。

多項式求逆

板子題連結
求逆指的是給定一個多項式 \(F(x)\),你需要求出一個多項式 \(G(x)\),使其滿足 \(F(x) * G(x)\equiv1\pmod {x^n}\)

考慮為什麼要在 \(\pmod {x^n}\) 情況下來求解,其實多項式可以近似理解為級數,顯然級數的項數是無窮的,所以我們用 \(\pmod {x^n}\) 來使得這個多項式的項數有窮。(大致理解就行)

首先大致的思路是分治解決問題。

顯然的如果多項式只有 \(1\) 項,那麼解就是其逆元。

我們考慮從原式在 mod \(x^{\lceil\frac n 2\rceil}\)

的推到 mod \(x^ n\) 的情 況:

\[F(x)* G_1(x)\equiv1\pmod {x^{\lceil\frac n 2\rceil}} \]

其中 \(G_1(x)\) 指在 \(\pmod {x^{\lceil\frac n 2\rceil}}\) 的情況下的解,\(G(x)\) 指在 \(\pmod {x^n}\) 的情況下的解。

那麼也有:

\[F(x)* G(x)\equiv1\pmod {x^{\lceil\frac n 2\rceil}} \]

兩個式子相減可以得到:

\[G_1(x)-G(x)\equiv0\pmod{x^{\lceil\frac n 2\rceil}} \]

平方得 :

\[G_1^{2}(x)-2G_1(x)G(x)+G^2(x)\equiv0\pmod{x^n} \]

兩邊同乘多項式 \(F(x)\) 得:

\[F(x) * G_1^{2}(x) - 2G_1(x) + G(x)\equiv0\pmod{x^n} \]

移項顯然得:

\[G(x)\equiv(2-F(x)G_1(x))G_1(x)\pmod{x^n} \]

然後你發現右邊的東西已經可以計算了。

複雜度非常顯然的 \(O(nlogn)\)

但是需要注意幾個問題,我會在程式碼裡寫出來。

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5, mod = 998244353, g1 = 3, g2 = (mod + 1) / 3;
int n, a[N], b[N], d[N], A[N], B[N];
inline int qpow(int a, int b, int res = 1) {
    for(; b; b >>= 1, a = 1ll * a * a % mod) 
        if(b & 1) res = 1ll * res * a % mod;
    return res;
}
void NTT(int *a, int flag, int len) {
    for(register int i = 0; i < len; ++i) if(i < d[i]) swap(a[i], a[d[i]]);
    for(register int l = 2, m = 1; l <= len; l <<= 1, m <<= 1) {
        int w1 = qpow(flag == 1 ? g1 : g2, (mod - 1) / l);
        for(register int *p = a; p != a + len; p += l) {
            int w = 1;
            for(register int i = 0; i < m; ++i, w = 1ll*w*w1%mod) {
                int t = 1ll * w * p[i + m] % mod;
                p[i + m] = (p[i] - t + mod) % mod, p[i] = (p[i] + t) % mod;
            }
        }
    }
}
void niyuan(int *a, int *b, int n) {
    memset(A, 0, n * 16), memset(B, 0, n * 16);//如果需要多次計算的話,別忘了清零
    b[0] = qpow(a[0], mod - 2);
    for(register int l = 2, m = 1; l <= n << 2; l <<= 1, m <<= 1) {//注意我們需要跑到 l < n * 4,即4倍
        for(register int i = 0; i < m; ++i) A[i] = a[i], B[i] = b[i];
        for(register int i = 0; i < l; ++i) d[i] = (d[i>>1] >> 1) | ((i & 1) ? m : 0);
        NTT(A, 1, l), NTT(B, 1, l);
        for(register int i = 0; i < l; ++i) b[i] = ((2ll - 1ll*A[i]*B[i]%mod) * B[i] % mod + mod) % mod;
        NTT(b, -1, l);
        int ni = qpow(l, mod - 2);
        for(register int i = 0; i < l; ++i) b[i] = 1ll * b[i] * ni % mod;
        for(register int i = m; i < l; ++i) b[i] = 0;//為什麼每次需要清零,主要是由推導的式子決定的。
    }
}
int main() {
    scanf("%d", &n);
    for(register int i = 0; i < n; ++i) scanf("%d", &a[i]);
    niyuan(a, b, n);
    for(register int i = 0; i < n; ++i) printf("%d ", b[i]);
    return 0;
}