Evaluate a polynomial at roots of unity, multiply pointwise, interpolate back — turning convolution into .

The three steps

evaluate  A and B at the n-th roots of unity     O(n log n)
multiply  pointwise                              O(n)
interpolate (inverse transform)                  O(n log n)

Evaluation and interpolation are the same algorithm with replaced by and a final division by . See Cooley-Tukey for the derivation.

NTT — the one to use

Over with , a primitive root gives a -th root of unity . Everything is exact integer arithmetic — no precision worries at all.

const long long MOD = 998244353, ROOT = 3;          // 998244353 = 119 * 2^23 + 1
 
void ntt(vector<long long>& 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) {
        long long w = powmod(ROOT, (MOD - 1) / len, MOD);
        if (invert) w = powmod(w, MOD - 2, MOD);
        for (int i = 0; i < n; i += len) {
            long long wn = 1;
            for (int j = 0; j < len / 2; j++) {
                long long u = a[i+j], v = a[i+j+len/2] * wn % MOD;
                a[i+j]           = (u + v) % MOD;
                a[i+j+len/2]     = (u - v + MOD) % MOD;
                wn = wn * w % MOD;
            }
        }
    }
    if (invert) {
        long long ninv = powmod(n, MOD - 2, MOD);
        for (auto& x : a) x = x * ninv % MOD;
    }
}

Identical in shape to the complex FFT — only the root of unity changes.

NTT-friendly primes

PrimeFactorisationMax lengthRoot
9982443533
10045358093
4697620493
201326592131
41793404541998202893

is not NTT-friendly (), which is why has become the standard contest modulus.

Arbitrary modulus

When the required modulus is not NTT-friendly:

  1. Three primes + CRT. Run NTT modulo three of the primes above; the true product is below , so CRT reconstructs it exactly, then reduce.
  2. Coefficient splitting. Write , similarly for ; four complex FFTs (or three with packing) give the exact product.
  3. __int128 accumulation for small .

Method 1 is the more robust; method 2 avoids modular arithmetic entirely.

FFT vs NTT

Complex FFTNTT
Arithmeticdoubleintegers mod
Exactnessapproximateexact
Precision limitsreal, and easy to hitnone
Speedcomparableslightly slower per butterfly
Modulus requirednoyes (or CRT)
Real-valued inputsnaturalneeds a modulus

Use NTT unless the coefficients are genuinely real.

Optimisations worth having

  • Precompute the roots once into an array; recomputing powmod per level is a large constant.
  • Avoid the modulo by using unsigned long long and reducing lazily (Barrett or Montgomery).
  • Skip the final inverse transform when you only need one coefficient.
  • Reuse transforms — squaring needs one forward transform, not two.
  • Pack two real sequences into one complex FFT (real in the real part, the other in the imaginary part).

What builds on it

OperationCost
Polynomial multiplication
Series inverse, log, exp, sqrt, pow
Polynomial division and remainder
Multipoint evaluation and interpolation
GCD of polynomials (half-GCD)
Linear recurrence, $n$-th term
Big integer multiplication
String matching with wildcards
Counting with generating functions

See also: NTT · Cooley-Tukey · Convolution