Opérations sur les polynômes

Opérations de multiplication polynomiale

FFT

#include <iostream>
#include <vector>
#include <cmath>
#include <complex>
using namespace std;
const int MAX_SIZE = 1 << 20;
typedef complex<double> base;

void fft(vector<base>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        double angle = 2 * M_PI / len * (invert ? -1 : 1);
        base wlen(cos(angle), sin(angle));
        
        for (int i = 0; i < n; i += len) {
            base w(1);
            for (int j = 0; j < len / 2; j++) {
                base u = a[i + j];
                base v = a[i + j + len / 2] * w;
                a[i + j] = u + v;
                a[i + j + len / 2] = u - v;
                w *= wlen;
            }
        }
    }
    
    if (invert) {
        for (int i = 0; i < n; i++)
            a[i] /= n;
    }
}

vector<int> multiply(vector<int> const& a, vector<int> const& b) {
    vector<base> fa(a.begin(), a.end()), fb(b.begin(), b.end());
    
    int n = 1;
    while (n < max(a.size(), b.size()))
        n <<= 1;
    n <<= 1;
    
    fa.resize(n);
    fb.resize(n);
    
    fft(fa, false);
    fft(fb, false);
    
    for (int i = 0; i < n; i++)
        fa[i] *= fb[i];
    
    fft(fa, true);
    
    vector<int> result(n);
    for (int i = 0; i < n; i++)
        result[i] = int(fa[i].real() + 0.5);
    
    return result;
}

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n + 1), b(m + 1);
    for (int i = 0; i <= n; i++)
        cin >> a[i];
    for (int i = 0; i <= m; i++)
        cin >> b[i];
    
    vector<int> result = multiply(a, b);
    
    for (int i = 0; i < result.size(); i++)
        cout << result[i] << " ";
    
    return 0;
}

NTT

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> multiply_ntt(vector<int> const& a, vector<int> const& b) {
    vector<int> fa = a, fb = b;
    
    int n = 1;
    while (n < max(a.size(), b.size()))
        n <<= 1;
    
    fa.resize(n);
    fb.resize(n);
    
    ntt(fa, false);
    ntt(fb, false);
    
    for (int i = 0; i < n; i++)
        fa[i] = (1LL * fa[i] * fb[i]) % MOD;
    
    ntt(fa, true);
    
    return fa;
}

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n + 1), b(m + 1);
    for (int i = 0; i <= n; i++)
        cin >> a[i];
    for (int i = 0; i <= m; i++)
        cin >> b[i];
    
    vector<int> result = multiply_ntt(a, b);
    
    for (int i = 0; i < result.size(); i++)
        cout << result[i] << " ";
    
    return 0;
}

Inverse polynomiale

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> inverse_poly(vector<int> const& a) {
    int n = a.size();
    vector<int> res(1);
    res[0] = mod_pow(a[0], MOD - 2, MOD);
    
    while (res.size() < n) {
        int cur_size = res.size();
        int next_size = min(2 * cur_size, n);
        
        vector<int> a_part(a.begin(), a.begin() + next_size);
        vector<int> res_part = res;
        res_part.resize(next_size);
        
        ntt(a_part, false);
        ntt(res_part, false);
        
        for (int i = 0; i < next_size; i++) {
            res_part[i] = (2LL * res_part[i] - (1LL * a_part[i] * res_part[i] % MOD) * res_part[i] % MOD + MOD) % MOD;
        }
        
        ntt(res_part, true);
        
        res = res_part;
    }
    
    return res;
}

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    vector<int> result = inverse_poly(a);
    
    for (int i = 0; i < n; i++)
        cout << result[i] << " ";
    
    return 0;
}

Racine carrée polynomiale

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;
const int HALF_INV = 499122177;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> sqrt_poly(vector<int> const& a) {
    int n = a.size();
    vector<int> res(1);
    res[0] = 1;
    
    while (res.size() < n) {
        int cur_size = res.size();
        int next_size = min(2 * cur_size, n);
        
        vector<int> a_part(a.begin(), a.begin() + next_size);
        vector<int> res_part = res;
        res_part.resize(next_size);
        
        ntt(a_part, false);
        ntt(res_part, false);
        
        vector<int> inv_res = res_part;
        ntt(inv_res, true);
        inv_res.resize(next_size);
        ntt(inv_res, false);
        
        for (int i = 0; i < next_size; i++) {
            inv_res[i] = (1LL * inv_res[i] * a_part[i]) % MOD;
        }
        ntt(inv_res, true);
        
        for (int i = 0; i < next_size; i++) {
            res_part[i] = (res_part[i] + inv_res[i]) % MOD;
            res_part[i] = (1LL * res_part[i] * HALF_INV) % MOD;
        }
        
        res = res_part;
    }
    
    return res;
}

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    vector<int> result = sqrt_poly(a);
    
    for (int i = 0; i < n; i++)
        cout << result[i] << " ";
    
    return 0;
}

Division polynomiale

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

pair<vector<int>, vector<int>> divide_poly(vector<int> const& a, vector<int> const& b) {
    int n = a.size() - 1;
    int m = b.size() - 1;
    
    if (n < m) {
        return {{}, a};
    }
    
    vector<int> f_rev(a.rbegin(), a.rend());
    vector<int> g_rev(b.rbegin(), b.rend());
    
    vector<int> inv_g = inverse_poly(g_rev);
    
    vector<int> q_rev(f_rev.size() - g_rev.size() + 1);
    for (int i = 0; i < q_rev.size(); i++) {
        q_rev[i] = 0;
        for (int j = 0; j <= i && j < inv_g.size(); j++) {
            q_rev[i] = (q_rev[i] + (1LL * f_rev[i - j] * inv_g[j]) % MOD) % MOD;
        }
    }
    
    vector<int> q(q_rev.rbegin(), q_rev.rend());
    
    vector<int> r(a);
    vector<int> bq(b.size() + q.size() - 1);
    for (int i = 0; i < b.size(); i++) {
        for (int j = 0; j < q.size(); j++) {
            if (i + j < bq.size()) {
                bq[i + j] = (bq[i + j] + (1LL * b[i] * q[j]) % MOD) % MOD;
            }
        }
    }
    
    for (int i = 0; i < r.size() && i < bq.size(); i++) {
        r[i] = (r[i] - bq[i] + MOD) % MOD;
    }
    r.resize(m);
    
    return {q, r};
}

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n + 1), b(m + 1);
    for (int i = 0; i <= n; i++)
        cin >> a[i];
    for (int i = 0; i <= m; i++)
        cin >> b[i];
    
    auto result = divide_poly(a, b);
    
    for (int i = 0; i < result.first.size(); i++)
        cout << result.first[i] << " ";
    cout << endl;
    
    for (int i = 0; i < result.second.size(); i++)
        cout << result.second[i] << " ";
    
    return 0;
}

Logarithme polynomiale

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> derivative(vector<int> const& a) {
    vector<int> res(a.size() - 1);
    for (int i = 1; i < a.size(); i++) {
        res[i - 1] = (1LL * a[i] * i) % MOD;
    }
    return res;
}

vector<int> integrate(vector<int> const& a) {
    vector<int> res(a.size() + 1);
    res[0] = 0;
    for (int i = 0; i < a.size(); i++) {
        res[i + 1] = (1LL * a[i] * mod_pow(i + 1, MOD - 2, MOD)) % MOD;
    }
    return res;
}

vector<int> log_poly(vector<int> const& a) {
    if (a[0] != 1) {
        throw invalid_argument("Le polynôme doit avoir une constante de 1 pour le logarithme");
    }
    
    vector<int> deriv = derivative(a);
    vector<int> inv_a = inverse_poly(a);
    
    vector<int> product(deriv.size() + inv_a.size() - 1);
    for (int i = 0; i < deriv.size(); i++) {
        for (int j = 0; j < inv_a.size(); j++) {
            if (i + j < product.size()) {
                product[i + j] = (product[i + j] + (1LL * deriv[i] * inv_a[j]) % MOD) % MOD;
            }
        }
    }
    
    return integrate(product);
}

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    vector<int> result = log_poly(a);
    
    for (int i = 0; i < n; i++)
        cout << result[i] << " ";
    
    return 0;
}

Exponentielle polynomiale

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> log_poly(vector<int> const& a) {
    if (a[0] != 1) {
        throw invalid_argument("Le polynôme doit avoir une constante de 1 pour le logarithme");
    }
    
    vector<int> deriv = derivative(a);
    vector<int> inv_a = inverse_poly(a);
    
    vector<int> product(deriv.size() + inv_a.size() - 1);
    for (int i = 0; i < deriv.size(); i++) {
        for (int j = 0; j < inv_a.size(); j++) {
            if (i + j < product.size()) {
                product[i + j] = (product[i + j] + (1LL * deriv[i] * inv_a[j]) % MOD) % MOD;
            }
        }
    }
    
    return integrate(product);
}

vector<int> exp_poly(vector<int> const& a) {
    if (a[0] != 0) {
        throw invalid_argument("Le polynôme doit avoir une constante de 0 pour l'exponentielle");
    }
    
    int n = a.size();
    vector<int> res(1);
    res[0] = 1;
    
    while (res.size() < n) {
        int cur_size = res.size();
        int next_size = min(2 * cur_size, n);
        
        vector<int> a_part(a.begin(), a.begin() + next_size);
        vector<int> res_part = res;
        res_part.resize(next_size);
        
        vector<int> log_res = log_poly(res_part);
        for (int i = 0; i < next_size; i++) {
            log_res[i] = (log_res[i] + a_part[i]) % MOD;
        }
        log_res[0] = (log_res[0] + 1) % MOD;
        
        vector<int> exp_res = vector<int>(next_size);
        for (int i = 0; i < next_size; i++) {
            exp_res[i] = 0;
            for (int j = 0; j <= i && j < res_part.size(); j++) {
                exp_res[i] = (exp_res[i] + (1LL * res_part[i - j] * log_res[j]) % MOD) % MOD;
            }
        }
        
        res = exp_res;
    }
    
    return res;
}

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    vector<int> result = exp_poly(a);
    
    for (int i = 0; i < n; i++)
        cout << result[i] << " ";
    
    return 0;
}

Puissence polynomiale rapide

#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int ROOT = 3;
const int ROOT_INV = 332748118;

int mod_pow(int base, int exp, int mod) {
    int result = 1;
    while (exp > 0) {
        if (exp % 2 == 1)
            result = (1LL * result * base) % mod;
        base = (1LL * base * base) % mod;
        exp /= 2;
    }
    return result;
}

void ntt(vector<int>& a, bool invert) {
    int n = a.size();
    
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1)
            j ^= bit;
        j ^= bit;
        
        if (i < j)
            swap(a[i], a[j]);
    }
    
    for (int len = 2; len <= n; len <<= 1) {
        int wlen = invert ? mod_pow(ROOT_INV, (MOD - 1) / len, MOD) 
                         : mod_pow(ROOT, (MOD - 1) / len, MOD);
        
        for (int i = 0; i < n; i += len) {
            int w = 1;
            for (int j = 0; j < len / 2; j++) {
                int u = a[i + j];
                int v = (1LL * w * a[i + j + len / 2]) % MOD;
                a[i + j] = (u + v) % MOD;
                a[i + j + len / 2] = (u - v + MOD) % MOD;
                w = (1LL * w * wlen) % MOD;
            }
        }
    }
    
    if (invert) {
        int inv_n = mod_pow(n, MOD - 2, MOD);
        for (int i = 0; i < n; i++)
            a[i] = (1LL * a[i] * inv_n) % MOD;
    }
}

vector<int> log_poly(vector<int> const& a) {
    if (a[0] != 1) {
        throw invalid_argument("Le polynôme doit avoir une constante de 1 pour le logarithme");
    }
    
    vector<int> deriv = derivative(a);
    vector<int> inv_a = inverse_poly(a);
    
    vector<int> product(deriv.size() + inv_a.size() - 1);
    for (int i = 0; i < deriv.size(); i++) {
        for (int j = 0; j < inv_a.size(); j++) {
            if (i + j < product.size()) {
                product[i + j] = (product[i + j] + (1LL * deriv[i] * inv_a[j]) % MOD) % MOD;
            }
        }
    }
    
    return integrate(product);
}

vector<int> exp_poly(vector<int> const& a) {
    if (a[0] != 0) {
        throw invalid_argument("Le polynôme doit avoir une constante de 0 pour l'exponentielle");
    }
    
    int n = a.size();
    vector<int> res(1);
    res[0] = 1;
    
    while (res.size() < n) {
        int cur_size = res.size();
        int next_size = min(2 * cur_size, n);
        
        vector<int> a_part(a.begin(), a.begin() + next_size);
        vector<int> res_part = res;
        res_part.resize(next_size);
        
        vector<int> log_res = log_poly(res_part);
        for (int i = 0; i < next_size; i++) {
            log_res[i] = (log_res[i] + a_part[i]) % MOD;
        }
        log_res[0] = (log_res[0] + 1) % MOD;
        
        vector<int> exp_res = vector<int>(next_size);
        for (int i = 0; i < next_size; i++) {
            exp_res[i] = 0;
            for (int j = 0; j <= i && j < res_part.size(); j++) {
                exp_res[i] = (exp_res[i] + (1LL * res_part[i - j] * log_res[j]) % MOD) % MOD;
            }
        }
        
        res = exp_res;
    }
    
    return res;
}

vector<int> pow_poly(vector<int> const& a, int power) {
    if (a[0] == 0) {
        throw invalid_argument("La base ne peut pas avoir une constante de 0");
    }
    
    int n = a.size();
    vector<int> log_a = log_poly(a);
    
    for (int i = 0; i < n; i++) {
        log_a[i] = (1LL * log_a[i] * power) % MOD;
    }
    
    return exp_poly(log_a);
}

int main() {
    int n, power;
    cin >> n >> power;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    
    vector<int> result = pow_poly(a, power);
    
    for (int i = 0; i < n; i++)
        cout << result[i] << " ";
    
    return 0;
}

Étiquettes: FFT NTT transformée de Fourier rapide arithmétique modulaire algorithmes polynomiaux

Publié le 19 juin à 01h12