The operations and their costs
| Operation | Naive | Fast | Method |
|---|---|---|---|
| Add / subtract | coefficientwise | ||
| Scalar multiply | |||
| Multiply | FFT/NTT | ||
| Divide by | synthetic division | ||
| Divide with remainder | Newton on the reversed polynomial | ||
| Evaluate at one point | Horner | ||
| Evaluate at points | multipoint evaluation | ||
| Interpolate from points | inverse multipoint | ||
| Derivative / integral | coefficientwise | ||
| GCD | half-GCD | ||
| Composition | Brent-Kung | ||
| Power | |||
| Shift | a convolution with binomials |
Horner’s method
long long evaluate(const vector<long long>& a, long long x, long long MOD) {
long long res = 0;
for (int i = a.size() - 1; i >= 0; i--) res = (res * x + a[i]) % MOD;
return res;
}multiplications and additions — optimal for a single evaluation, and the standard way to compute a polynomial hash.
Division with remainder
with . Naive long division is ; the fast version uses the reversal trick:
so computed as a power series inverse — . Then .
// rev(P)[i] = P[deg - i]
pair<Poly,Poly> divmod(Poly a, Poly b) {
int d = a.size() - b.size();
if (d < 0) return {{0}, a};
Poly ra = reversed(a), rb = reversed(b);
Poly q = reversed(mulTrunc(ra, inverse(rb, d + 1), d + 1));
Poly r = sub(a, mul(q, b));
r.resize(b.size() - 1);
return {q, r};
}Synthetic division by
// divide by (x - a); returns the quotient, and the remainder is P(a)
vector<long long> synthetic(vector<long long> p, long long a, long long MOD) {
for (int i = p.size() - 2; i >= 0; i--)
p[i] = (p[i] + a * p[i+1]) % MOD;
long long rem = p[0];
p.erase(p.begin());
return p; // remainder = rem = P(a)
}, and it is the same recurrence as Horner — evaluation and division by a linear factor are the same computation.
Roots and factorisation
| Task | Method |
|---|---|
| Rational roots over | rational root theorem: , |
| Roots modulo a prime | , then Cantor-Zassenhaus |
| Real roots | Sturm sequences + bisection |
| All complex roots | Durand-Kerner, or companion-matrix eigenvalues |
| Squarefree part | |
| Full factorisation over | SFF → DDF → EDF |
| Factorisation over | factor mod , Hensel lift, recombine |
Useful identities
The last one (Vieta’s formulas) converts between roots and coefficients, and the product is computable in by divide and conquer — the same recursion used for multipoint evaluation.
// product of (x - r_i) in O(n log^2 n)
Poly buildProduct(int lo, int hi) {
if (hi - lo == 1) return {MOD - r[lo], 1};
int mid = (lo + hi) / 2;
return mul(buildProduct(lo, mid), buildProduct(mid, hi));
}Representation
For contest use, a vector<long long> with index = degree (little-endian) is the right choice: addition is a loop, multiplication feeds straight into NTT, and the derivative is a shift.
Trim trailing zeros after every operation, or the degree bookkeeping drifts.
See also: FFT and NTT · Formal Power Series · Lagrange Interpolation