Polynomial Operations (over a Field, especially mod p) — Practice Tasks¶
All tasks must be solved in Go, Java, and Python. Each task ships with starter code or a precise spec in all three languages. Implement the polynomial logic and validate against the evaluation criteria. Always use a prime modulus (
998244353) so inverses exist, and test multiply/division against a schoolbook oracle on small inputs before trusting fast code.
Beginner Tasks (5)¶
Task 1 — Add and scale polynomials mod p¶
Problem. Implement add(A, B, p) and scale(A, c, p) for coefficient arrays over F_p. add pads the shorter array with zeros; scale multiplies every coefficient by c. Reduce mod p after each operation.
Input / Output spec. - Read n, then A (n ints); read m, then B (m ints); read scalar c. - Print add(A, B) then scale(A, c), each space-separated.
Constraints. 1 ≤ n, m ≤ 10^5, entries in [0, p), p = 10^9 + 7.
Hint. Treat missing indices as 0. Subtraction (if you add it) must normalize non-negative.
Starter — Go.
package main
import "fmt"
const MOD = 1_000_000_007
func add(a, b []int64) []int64 {
// TODO: pad shorter, add element-wise mod MOD
return nil
}
func scale(a []int64, c int64) []int64 {
// TODO: multiply each coeff by c mod MOD
return nil
}
func main() {
var n int
fmt.Scan(&n)
a := make([]int64, n)
for i := range a {
fmt.Scan(&a[i])
}
var m int
fmt.Scan(&m)
b := make([]int64, m)
for i := range b {
fmt.Scan(&b[i])
}
var c int64
fmt.Scan(&c)
for _, v := range add(a, b) {
fmt.Print(v, " ")
}
fmt.Println()
for _, v := range scale(a, c) {
fmt.Print(v, " ")
}
fmt.Println()
}
Starter — Java.
import java.util.*;
public class Task1 {
static final long MOD = 1_000_000_007L;
static long[] add(long[] a, long[] b) {
// TODO
return null;
}
static long[] scale(long[] a, long c) {
// TODO
return null;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] a = new long[n];
for (int i = 0; i < n; i++) a[i] = sc.nextLong();
int m = sc.nextInt();
long[] b = new long[m];
for (int i = 0; i < m; i++) b[i] = sc.nextLong();
long c = sc.nextLong();
System.out.println(Arrays.toString(add(a, b)));
System.out.println(Arrays.toString(scale(a, c)));
}
}
Starter — Python.
import sys
MOD = 1_000_000_007
def add(a, b):
# TODO
return []
def scale(a, c):
# TODO
return []
def main():
data = iter(sys.stdin.read().split())
n = int(next(data))
a = [int(next(data)) for _ in range(n)]
m = int(next(data))
b = [int(next(data)) for _ in range(m)]
c = int(next(data))
print(*add(a, b))
print(*scale(a, c))
if __name__ == "__main__":
main()
Evaluation criteria. - Correct element-wise add with padding. - scale reduces mod p. - No overflow (64-bit before % MOD).
Task 2 — Schoolbook multiplication mod p¶
Problem. Implement multiply(A, B, p) returning the convolution (A·B)[k] = Σ_{i+j=k} A[i]·B[j] mod p. Output length is len(A)+len(B)−1.
Input / Output spec. - Read n, A, then m, B. - Print the product coefficients, space-separated.
Constraints. 1 ≤ n, m ≤ 2000, p = 998244353.
Hint. Double loop, i, j; reduce after each accumulation; skip A[i] == 0.
Reference oracle (Python).
def brute_mul(a, b, mod=998244353):
r = [0] * (len(a) + len(b) - 1)
for i in range(len(a)):
for j in range(len(b)):
r[i + j] = (r[i + j] + a[i] * b[j]) % mod
return r
Evaluation criteria. - Matches brute_mul and direct hand expansion on a 2×2 example. - Correct product length n+m−1. - O(n·m).
Task 3 — Horner evaluation at a point¶
Problem. Given A and a point x0, compute A(x0) mod p using Horner's rule.
Input / Output spec. - Read n, A, then x0. Print A(x0) mod p.
Constraints. 1 ≤ n ≤ 10^5, 0 ≤ x0 < p, p = 998244353.
Hint. acc = 0; for i from high to low: acc = (acc*x0 + A[i]) % p.
Worked check. For A = [3, 5, 2] (3+5x+2x²) and x0 = 2, the answer is 21. For any A, A(0) = A[0].
Evaluation criteria. - A(0) equals the constant term. - Matches the naive Σ A[i]·x0^i mod p for small inputs. - O(n) — no recomputing powers of x0.
Task 4 — Formal derivative¶
Problem. Given A, output its formal derivative A' where A'[i−1] = i·A[i] mod p.
Input / Output spec. - Read n, A. Print A' (length n−1, or [0] if n ≤ 1).
Constraints. 1 ≤ n ≤ 10^5, p = 998244353.
Hint. A'[i-1] = (i * A[i]) % p for i ≥ 1. The constant term drops out.
Worked check. For A = [3, 5, 2] (3+5x+2x²), A' = [5, 4] (5 + 4x).
Evaluation criteria. - Constant polynomial → derivative [0]. - A'[i-1] = i·A[i] reduced mod p. - O(n).
Task 5 — Evaluate at multiple points (naive Horner loop)¶
Problem. Given A and a list of query points xs, output A(x) mod p for each x using Horner once per point.
Input / Output spec. - Read n, A, then q, then q query points. - Print the q answers, space-separated.
Constraints. 1 ≤ n ≤ 2000, 1 ≤ q ≤ 2000, p = 998244353.
Hint. Reuse your Task 3 Horner. Total cost O(n·q). (For large n and q, multipoint evaluation is O(n log² n) — see Task 13.)
Evaluation criteria. - Each answer matches a single Horner evaluation. - Handles q points without recomputing the polynomial. - O(n·q).
Intermediate Tasks (5)¶
Task 6 — Long division with remainder¶
Problem. Given A and B (B ≠ 0) over F_p, output (Q, R) with A = B·Q + R, deg R < deg B. The modulus is prime so lc(B) is invertible.
Input / Output spec. - Read A, then B. Print Q on one line, R on the next.
Constraints. 1 ≤ len(A), len(B) ≤ 2000, p = 998244353.
Hint. Compute modinv(B[-1]) via Fermat (pow(b, p-2, p)). For k from deg A − deg B down to 0, set quotient coefficient and subtract a scaled shift of B from the running remainder.
Reference oracle (Python).
def verify_divmod(A, B, Q, R, mod=998244353):
# check A == B*Q + R and deg R < deg B
prod = brute_mul(B, Q, mod)
while len(prod) < max(len(A), len(R)):
prod.append(0)
s = [(prod[i] + (R[i] if i < len(R) else 0)) % mod for i in range(len(prod))]
a = [(A[i] if i < len(A) else 0) % mod for i in range(len(prod))]
return s == a and (len(R) < len(B) or all(c == 0 for c in R))
Evaluation criteria. - A == B·Q + R and deg R < deg B (use the oracle). - Returns Q = [0], R = A when deg A < deg B. - O(n·m).
Task 7 — Lagrange interpolation: value at a point¶
Problem. Given n+1 points with distinct x_i and a target x*, compute P(x*) mod p for the unique degree-≤ n interpolant.
Input / Output spec. - Read k (= number of points), then k pairs (x_i, y_i), then x*. Print P(x*) mod p.
Constraints. 1 ≤ k ≤ 2000, distinct x_i, p = 998244353.
Hint. P(x*) = Σ_i y_i · Π_{j≠i}(x* − x_j)/(x_i − x_j). Compute each term's numerator and denominator products, then num · modinv(den).
Worked check. Points on x²+1: (0,1),(1,2),(2,5); P(3) = 10. Evaluating at any input node returns that node's y_i.
Evaluation criteria. - Re-evaluating at an input x_i returns y_i. - Matches the known polynomial on test cases. - Asserts distinct x_i.
Task 8 — Newton divided differences (incremental)¶
Problem. Build the Newton form (divided-difference coefficients) for n+1 points, then support evaluating P(x) and adding a new point in O(n).
Input / Output spec. - Read points, build coefficients; read a target x and print P(x); then read a new point, add it, and print the updated P(x) at the same target.
Constraints. 1 ≤ k ≤ 2000, distinct x_i, p = 998244353.
Hint. c[i] is f[x_0,…,x_i]. Build the table in O(n²). Evaluate with the nested form acc = acc·(x − x_i) + c[i] over the x_i high→low. Adding a point appends one c and one node.
Evaluation criteria. - Matches Lagrange on the same points. - Adding a point costs O(n), not a full rebuild. - Re-evaluation at input nodes recovers y_i.
Task 9 — Polynomial GCD (monic)¶
Problem. Given A and B over F_p, output their monic GCD via the Euclidean algorithm.
Input / Output spec. - Read A, then B. Print the monic GCD.
Constraints. 1 ≤ len(A), len(B) ≤ 2000, p = 998244353.
Hint. while B ≠ 0: (A, B) = (B, A mod B); then divide by the leading coefficient to make monic. Reuse the Task 6 remainder.
Worked check. A = (x−1)(x−2) = x²−3x+2, B = (x−1)(x−3) = x²−4x+3 → monic GCD x − 1. gcd(A, A) = monic(A).
Evaluation criteria. - Result is monic and divides both inputs. - Matches a brute-force common-factor check on small inputs. - Terminates (remainder degree strictly decreases).
Task 10 — Square-free detection via gcd(P, P')¶
Problem. Decide whether P is square-free (no repeated factor) by testing whether gcd(P, P') is a nonzero constant. Output SQUARE_FREE or HAS_REPEATED_FACTOR.
Input / Output spec. - Read P. Print the verdict.
Constraints. 1 ≤ len(P) ≤ 2000, p = 998244353, and p > deg P (so the derivative criterion is valid).
Hint. Compute P' (Task 4), then gcd(P, P') (Task 9). If the GCD has degree 0 (a constant), P is square-free; otherwise it has a repeated factor.
Worked check. (x−1)² = x²−2x+1 → HAS_REPEATED_FACTOR. (x−1)(x−2) → SQUARE_FREE.
Evaluation criteria. - Correct on (x−1)² (repeated) and (x−1)(x−2) (square-free). - Uses the gcd(P, P') criterion, not factorization. - Documents the p > deg P requirement in a comment.
Advanced Tasks (5)¶
Task 11 — NTT-based multiplication for large degree¶
Problem. Multiply two polynomials of degree up to 2·10^5 over F_p (p = 998244353) in O(n log n) using the Number-Theoretic Transform.
Constraints. 1 ≤ len(A), len(B) ≤ 2·10^5, p = 998244353.
Hint. Pad to a power of two ≥ deg(A)+deg(B)+1. Forward-NTT both, multiply pointwise, inverse-NTT (remember to scale by the inverse of the length). The primitive root is 3 for 998244353. See sibling 15-ntt for the transform details.
Evaluation criteria. - Matches schoolbook brute_mul (Task 2 oracle) on random inputs up to length ~2000. - Correct inverse-transform scaling (no all-coefficients-times-n bug). - Runs in well under a second for length 2·10^5. - Pads to the next power of two, not a non-power-of-two length.
Task 12 — Series inverse mod x^n via Newton iteration¶
Problem. Given A with A(0) ≠ 0, compute B with A·B ≡ 1 (mod x^n), using Newton's doubling iteration on top of your multiply.
Constraints. 1 ≤ n ≤ 10^5, A[0] ≠ 0, p = 998244353.
Hint. Start B = [modinv(A[0])]. Double precision: B ← B·(2 − A·B) mod x^{2t}. Truncate to x^n at the end. Use NTT multiply (Task 11) for O(n log n).
Reference oracle (Python).
def verify_inverse(A, B, n, mod=998244353):
prod = brute_mul(A[:n], B[:n], mod)[:n]
return prod[0] % mod == 1 and all(c % mod == 0 for c in prod[1:])
Evaluation criteria. - A·B ≡ 1 (mod x^n) (use the oracle). - Fails fast / asserts when A[0] == 0. - O(n log n) with NTT multiply; log₂ n doubling steps.
Task 13 — Multipoint evaluation (subproduct tree)¶
Problem. Evaluate P (degree < n) at n arbitrary points in O(n log² n) using a subproduct tree, instead of O(n²) Horner-per-point.
Constraints. 1 ≤ n ≤ 5·10^4, distinct or repeated points allowed, p = 998244353.
Hint. Build a tree whose leaves are (x − x_i) and internal nodes are products of children (root = Π(x − x_i)). Recursively take P mod (left subtree) and P mod (right subtree) down the tree; at each leaf the remaining constant is P(x_i). Uses fast multiply and fast division.
Evaluation criteria. - Matches naive Horner-per-point (Task 5) for small n. - O(n log² n) — does not degrade to O(n²). - Correct for repeated query points.
Task 14 — Fast interpolation from n points¶
Problem. Given n points with distinct x_i, recover the coefficient array of the unique degree-< n interpolant in O(n log² n).
Constraints. 1 ≤ n ≤ 5·10^4, distinct x_i, p = 998244353.
Hint. Build the subproduct tree (Task 13). Compute the master polynomial M = Π(x − x_i) and its derivative M'; multipoint-evaluate M' at the nodes to get the Lagrange denominators d_i = M'(x_i). Then combine y_i / d_i bottom-up through the tree.
Evaluation criteria. - Re-evaluating the recovered polynomial at the input nodes returns the y_i. - Matches O(n²) Lagrange/Newton on small inputs. - O(n log² n).
Task 15 — Decide the right polynomial tool¶
Problem. Given a problem's parameters (operation, degree n, query_count, modulus_prime?), return one of: SCHOOLBOOK, NTT, KARATSUBA, NEWTON_SERIES, SUBPRODUCT_TREE, or NEEDS_PRIME_MODULUS. Justify each decision.
Constraints / cases to handle. - Multiply, small n (≤ ~2000) → SCHOOLBOOK. - Multiply, medium n → KARATSUBA. - Multiply, large n, prime modulus → NTT. - Series inverse/log/exp/sqrt → NEWTON_SERIES. - Multipoint eval / interpolation of many points → SUBPRODUCT_TREE. - Division/GCD/interpolation requested over a composite modulus → NEEDS_PRIME_MODULUS.
Hint. Encode the decision rules from middle.md and senior.md. The trap is the composite-modulus case: division needs inverses, which require a prime.
Evaluation criteria. - Correctly classifies all six canonical cases. - The NEEDS_PRIME_MODULUS branch explicitly cites that inverses (hence division/GCD/interpolation) require a field. - Justification references the right complexity (O(n²), O(n^1.585), O(n log n), O(n log² n)).
Benchmark Task¶
Task B — Schoolbook vs NTT crossover¶
Problem. Empirically find the degree n at which NTT multiplication overtakes schoolbook for polynomials over F_p. Implement two multipliers and time them:
- (a) Schoolbook:
O(n²)convolution with zero-skip. - (b) NTT:
O(n log n)(Task 11).
Measure wall-clock time for n ∈ {64, 256, 1024, 4096, 16384, 65536} on random coefficient arrays (fixed seed). Report a table and identify the crossover n.
Starter — Python.
import random
import time
from typing import List
MOD = 998244353
def gen_poly(n: int, seed: int) -> List[int]:
r = random.Random(seed)
return [r.randrange(MOD) for _ in range(n)]
def schoolbook(a, b):
# TODO: O(n^2) convolution mod MOD, zero-skip
return [0] * (len(a) + len(b) - 1)
def ntt_mul(a, b):
# TODO: O(n log n) NTT-based multiply mod MOD
return [0] * (len(a) + len(b) - 1)
def bench(fn, a, b) -> float:
start = time.perf_counter()
fn(a, b)
return time.perf_counter() - start
def mean_ms(samples: List[float]) -> float:
return sum(samples) / len(samples) * 1000.0
def main() -> None:
print("n schoolbook_ms ntt_ms")
for n in [64, 256, 1024, 4096, 16384, 65536]:
a = gen_poly(n, 1)
b = gen_poly(n, 2)
ntt_runs = [bench(ntt_mul, a[:], b[:]) for _ in range(3)]
if n <= 4096:
sb_runs = [bench(schoolbook, a[:], b[:]) for _ in range(3)]
print(f"{n:<8d} {mean_ms(sb_runs):<18.2f} {mean_ms(ntt_runs):<10.2f}")
else:
print(f"{n:<8d} {'(skipped)':<18} {mean_ms(ntt_runs):<10.2f}")
if __name__ == "__main__":
main()
Evaluation criteria. - Same seed produces the same polynomials across Go, Java, and Python. - Schoolbook (a) wins for small n; NTT (b) wins for large n. Report the crossover n. - NTT completes for n = 65536 in well under a second; schoolbook is infeasible there. - Both multipliers agree on outputs for n up to ~2000. - Report includes the mean across at least 3 runs and does not time generation or cloning.
Task B2 — Newton-series round-trip stress test¶
Problem. Validate a series-operation library by round-trip identities on random inputs: for random A with the right constant term, check A·inverse(A) ≡ 1, exp(log(A)) ≡ A (with A(0) = 1), and sqrt(A)² ≡ A (with A(0) a quadratic residue), all mod x^n.
Constraints. 1 ≤ n ≤ 10^4, p = 998244353.
Hint. Generate A with the required constant term, run the operation, and verify the identity coefficient-by-coefficient. A single failing coefficient localizes the bug (e.g. a missing inverse-transform scaling shows every coefficient off by a factor of the transform length).
Evaluation criteria. - All three round-trips hold for random inputs at several n. - The harness reports the first failing coefficient index when an identity breaks. - Runs in O(n log n) per check with NTT multiply. - Cross-language identical results on the same seed.
General Guidance for All Tasks¶
- Always use a prime modulus.
998244353if NTT may follow;10^9 + 7otherwise. Division, GCD, and interpolation all require modular inverses, which exist for every nonzero element only over a field. - Pin the modulus as a named constant. Never inline magic numbers.
- Get the product length right.
len(A·B) = len(A) + len(B) − 1; off-by-one here leaves a stray trailing zero. - Normalize / trim trailing zeros before reading the degree, especially after subtraction in division and GCD.
- Mind overflow. In Go/Java cast to 64-bit before multiplying, then reduce; normalize subtraction non-negative.
- Validate against the schoolbook oracle. Every multiply/division/interpolation task above ships with (or references) a slow oracle; diff coefficient-by-coefficient on small inputs before trusting fast code on large inputs.
- Watch series preconditions. Inverse needs
A(0) ≠ 0;expneedsA(0) = 0;logneedsA(0) = 1;sqrtneedsA(0)a quadratic residue. - Round-trip to self-verify. When no external oracle exists at scale, check identities like
A·A^{-1} ≡ 1,exp(log A) ≡ A, andB·Q + R ≡ A; a broken identity pinpoints the failing operation.
Each task must be implemented and tested in Go, Java, and Python, with identical outputs across all three on the same seeded inputs.
In this topic
- interview
- tasks