洛谷P3803 快速数论变化(NTT)模板题

前置知识

欧拉函数 \(\varphi(n)\):小于或等于 \(n\) 的正整数中与 \(n\) 互质的数的个数。

费马小定理:

\(a^p \equiv a (\mod p)\)

推论:

\(a^{-1} \equiv 1 (\mod p)\)

欧拉定理

\(a^{\varphi(n)} \equiv 1(\mod n)\)

原根

满足同余式 \(a^n \equiv 1(\mod m)\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\)\(m\) 的阶,记作 \(\delta_m(a)\)

\(\gcd(a, m) = 1\)\(\delta_m(a) = \varphi(m)\),则称 \(a\) 为模 \(m\) 的原根。

FFT NTT
\(w_n^k = e^{\frac{-2k\pi}{n} i}\) \(g_n^k = ( G^\frac{p-1}{n} )^k \mod p\)

示例程序

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 998244353, G = 3, Gi = 332748118;

ll fpow(ll a, ll b) {
    ll res = 1;
    for (ll t = a % mod; b; b >>= 1, t = t * t % mod)
        if (b & 1ll)
            res = res * t % mod;
    return res;
}

ll inv(ll a) {
    return fpow(a, mod - 2);
}

void ntt(vector<ll>&a, int type) {
    int n = a.size();
    if (n == 1) return;

    vector<ll> a0(n/2), a1(n/2);
    for (int i = 0; i < n/2; i++) {
        a0[i] = a[i * 2];
        a1[i] = a[i * 2 + 1];
    }

    ntt(a0, type);
    ntt(a1, type);

    ll wn = fpow(type == 1 ? G : Gi, (mod - 1) / n);
    ll w = 1;

    for (int k = 0; k < n/2; k++) {
        ll t = w * a1[k] % mod;
        a[k] = (a0[k] + t) % mod;
        a[k + n/2] = (a0[k] - t + mod) % mod;
        w = w * wn % mod;
    }
}

int n, m, limit = 1;

int main() {
    scanf("%d%d", &n, &m);
    while (limit < n + m + 1)
        limit <<= 1;
    vector<ll> a(limit), b(limit);
    for (int i = 0; i <= n; i++) {
        scanf("%lld", &a[i]);
        a[i] %= mod;
    }
    for (int i = 0; i <= m; i++) {
        scanf("%lld", &b[i]);
        b[i] %= mod;
    }

    ntt(a, 1);
    ntt(b, 1);

    for (int i = 0; i < limit; i++)
        a[i] = a[i] * b[i] % mod;

    ntt(a, -1);

    ll invLimit = inv(limit);
    for (int i = 0; i <= n+m; i++)
        printf("%lld ", a[i] * invLimit % mod);

    return 0;
}

学习链接:

posted @ 2026-04-14 14:36  quanjun  阅读(3)  评论(0)    收藏  举报