Number Theory Basics
What this covers
A handful of number-theory routines appear repeatedly in contest problems. None is long, but each has a cost that decides whether it fits the constraints. Knowing the cost is as important as knowing the code.
Divisors
To find every divisor of n, loop only to the square root. Divisors pair up: if d divides n, so does n / d, and one of the pair is always at most √n.
static List<Long> divisors(long n) {
List<Long> result = new ArrayList<>();
for (long d = 1; d * d <= n; d++) {
if (n % d == 0) {
result.add(d);
if (d != n / d) { // avoid adding the square root twice
result.add(n / d);
}
}
}
return result;
}
This is O(√n). For n up to 1012 that is a million iterations — fine.
The condition d * d <= n rather than d <= Math.sqrt(n) avoids floating-point rounding at the boundary. The d != n / d guard prevents listing the square root twice for perfect squares.
How many divisors to expect
A number below 1018 has at most a few thousand divisors. So collecting them into a list is safe — you will not run out of memory. But the count of divisors grows irregularly, so never assume it is small in a complexity argument without checking.
Primality of a single number
Trial division to the square root, skipping even numbers after 2.
static boolean isPrime(long n) {
if (n < 2) return false;
if (n < 4) return true; // 2 and 3
if (n % 2 == 0) return false;
for (long d = 3; d * d <= n; d += 2) {
if (n % d == 0) return false;
}
return true;
}
O(√n). Good for n up to about 1014 as a one-off. If you need primality for many large numbers, use a Miller–Rabin test instead; for the ranges typical of contest problems, this suffices.
The early returns matter: n < 2 excludes 0, 1, and negatives, and 1 is not prime. That is a classic source of a wrong answer on the smallest test case.
Sieve of Eratosthenes
When you need every prime up to N, testing each individually is O(N√N). The sieve does it in roughly O(N log log N), which is effectively linear.
static boolean[] sieve(int n) {
boolean[] composite = new boolean[n + 1];
composite[0] = composite[1] = true;
for (int i = 2; (long) i * i <= n; i++) {
if (!composite[i]) {
for (int j = i * i; j <= n; j += i) {
composite[j] = true;
}
}
}
return composite; // composite[k] == false means k is prime
}
Two details carry the efficiency. The outer loop stops at √n, because any composite has a factor at or below its square root. The inner loop starts at i * i rather than 2 * i, because smaller multiples of i were already crossed off by smaller primes.
Note the (long) i * i cast in the outer condition — for n near 2 × 109, i * i would overflow int.
A sieve up to 107 uses about 10 MB as a boolean[] and runs in well under a second. Up to 108 it becomes tight on both.
Smallest prime factor
A variant that stores, for each number, its smallest prime factor. This makes factorizing any number in the range O(log n) with no division loop.
static int[] smallestPrimeFactor(int n) {
int[] spf = new int[n + 1];
for (int i = 2; i <= n; i++) {
if (spf[i] == 0) { // i is prime
for (int j = i; j <= n; j += i) {
if (spf[j] == 0) spf[j] = i;
}
}
}
return spf;
}
static List<Integer> factorize(int x, int[] spf) {
List<Integer> factors = new ArrayList<>();
while (x > 1) {
int p = spf[x];
while (x % p == 0) {
factors.add(p);
x /= p;
}
}
return factors;
}
Use this when a problem asks you to factorize many numbers all below some bound. Building the table once beats factorizing each number separately.
Factorizing a single large number
Without a precomputed table, divide out each factor as you find it.
static List<Long> primeFactors(long n) {
List<Long> factors = new ArrayList<>();
for (long d = 2; d * d <= n; d++) {
while (n % d == 0) {
factors.add(d);
n /= d;
}
}
if (n > 1) factors.add(n); // whatever remains is prime
return factors;
}
The final if (n > 1) is essential and easy to forget. After dividing out everything up to √n, any remainder greater than 1 is itself a prime larger than √n — for example factorizing 14 leaves 7 after removing 2.
This is O(√n) and returns factors with multiplicity, so 12 gives [2, 2, 3].
GCD and LCM
The Euclidean algorithm, which is O(log min(a, b)).
static long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
static long lcm(long a, long b) {
return a / gcd(a, b) * b; // divide first to avoid overflow
}
In lcm, dividing before multiplying matters. Writing a * b / gcd(a, b) computes the product first, which can overflow even when the final result would fit.
Java also provides java.math.BigInteger.gcd, but the three-line version is faster and avoids allocation.
Costs at a glance
| Task | Approach | Cost | Practical limit |
|---|---|---|---|
| All divisors of n | Loop to √n | O(√n) | n ≤ 1012 |
| Is n prime? | Trial division | O(√n) | n ≤ 1014 |
| All primes up to N | Sieve | O(N log log N) | N ≤ 107 |
| Factorize many small numbers | Smallest-prime-factor table | O(N log N) build, O(log x) each | N ≤ 107 |
| Factorize one large number | Divide out factors | O(√n) | n ≤ 1012 |
| GCD | Euclid | O(log n) | any long |
Common mistakes
- Treating 1 as prime. It is not. Check
n < 2first. - Forgetting the leftover factor after the factorization loop.
d <= Math.sqrt(n)instead ofd * d <= n, which can misbehave at the boundary.- Overflow in
lcm. Divide before multiplying. i * ioverflowingintin a sieve for largeN.- Sieving per query instead of once. Build the table before reading the queries.
- Assuming a number has few divisors without checking.
Practice
Pick the approach from the cost table before writing code.
- Print all primes up to 106.
- Given
nup to 1012, print its prime factorization. - Given 105 numbers, each up to 106, print the number of distinct prime factors of each.
- Given
n, print the number of divisors ofn!— no, print the number of trailing zeros ofn!in base 10, for n up to 1018. - Given an array, print the GCD of all elements, then the LCM modulo 109+7.
Hints
- Sieve, then scan.
- Divide out factors to √n, remembering the leftover.
- Build a smallest-prime-factor table once to 106, then factorize each query in O(log x). Count distinct primes, not multiplicity.
- Trailing zeros come from factors of 10, so count factors of 5 — there are always more 2s. Sum
n/5 + n/25 + n/125 + ..., stopping when the term reaches zero. Uselongand divide rather than computing powers. - GCD folds directly. LCM under a modulus is subtle: you cannot use the divide-by-GCD formula once values are reduced. Factorize each element and take the maximum exponent of each prime, then multiply those prime powers modulo the answer. See Modular Arithmetic.