Number Theory

Number theory deals with properties of integers and is fundamental for cryptography, competitive programming, and mathematical problem solving.

Prime Numbers

A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself.

Primality Testing

bool isPrime(long long n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    
    // Check for divisors from 5 to sqrt(n)
    for (long long i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}

Sieve of Eratosthenes

vector<bool> sieve(int n) {
    vector<bool> is_prime(n + 1, true);
    is_prime[0] = is_prime[1] = false;
    
    for (int i = 2; i * i <= n; i++) {
        if (is_prime[i]) {
            for (int j = i * i; j <= n; j += i) {
                is_prime[j] = false;
            }
        }
    }
    return is_prime;
}

// Get list of primes up to n
vector<int> getPrimes(int n) {
    vector<bool> is_prime = sieve(n);
    vector<int> primes;
    
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            primes.push_back(i);
        }
    }
    return primes;
}

Sieve Optimization:

Time complexity: O(n log log n). Space: O(n). For n ≤ 10^7, sieve is very efficient.

Prime Factorization

Trial Division Method

vector<pair<int, int>> factorize(int n) {
    vector<pair<int, int>> factors; // {prime, power}
    
    // Check for factor 2
    if (n % 2 == 0) {
        int power = 0;
        while (n % 2 == 0) {
            n /= 2;
            power++;
        }
        factors.push_back({2, power});
    }
    
    // Check for odd factors
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) {
            int power = 0;
            while (n % i == 0) {
                n /= i;
                power++;
            }
            factors.push_back({i, power});
        }
    }
    
    // If n is still > 1, then it's a prime
    if (n > 1) {
        factors.push_back({n, 1});
    }
    
    return factors;
}

Factorization using Precomputed Primes

vector<int> primes; // Precomputed primes

vector<pair<int, int>> fastFactorize(long long n) {
    vector<pair<int, int>> factors;
    
    for (int p : primes) {
        if ((long long)p * p > n) break;
        
        if (n % p == 0) {
            int power = 0;
            while (n % p == 0) {
                n /= p;
                power++;
            }
            factors.push_back({p, power});
        }
    }
    
    if (n > 1) {
        factors.push_back({(int)n, 1});
    }
    
    return factors;
}

GCD and LCM

Euclidean Algorithm for GCD

// Recursive GCD
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

// Iterative GCD
int gcd_iterative(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// LCM using GCD
long long lcm(int a, int b) {
    return (long long)a * b / gcd(a, b);
}

Extended Euclidean Algorithm

// Returns gcd(a, b) and finds x, y such that ax + by = gcd(a, b)
int extendedGCD(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    
    int x1, y1;
    int gcd = extendedGCD(b, a % b, x1, y1);
    
    x = y1;
    y = x1 - (a / b) * y1;
    
    return gcd;
}

Properties:

  • gcd(a, b) = gcd(b, a % b)
  • lcm(a, b) = a * b / gcd(a, b)
  • gcd(a, b) * lcm(a, b) = a * b
  • gcd(a, 0) = a

Modular Arithmetic

Basic Operations

const int MOD = 1e9 + 7;

// Safe modular addition
int addMod(int a, int b) {
    return ((a % MOD) + (b % MOD)) % MOD;
}

// Safe modular subtraction
int subMod(int a, int b) {
    return ((a % MOD) - (b % MOD) + MOD) % MOD;
}

// Safe modular multiplication
long long mulMod(long long a, long long b) {
    return ((a % MOD) * (b % MOD)) % MOD;
}

Fast Modular Exponentiation

long long fastPower(long long base, long long exp, long long mod) {
    long long result = 1;
    base %= mod;
    
    while (exp > 0) {
        if (exp & 1) {
            result = (result * base) % mod;
        }
        base = (base * base) % mod;
        exp >>= 1;
    }
    
    return result;
}

// Alternative using bit manipulation
long long power(long long a, long long b, long long m = MOD) {
    long long res = 1;
    a %= m;
    while (b > 0) {
        if (b & 1) res = res * a % m;
        a = a * a % m;
        b >>= 1;
    }
    return res;
}

Modular Inverse

Using Fermat's Little Theorem

If p is prime and gcd(a, p) = 1, then a^(p-1) ≡ 1 (mod p), so a^(p-2) ≡ a^(-1) (mod p)

long long modInverse(long long a, long long p) {
    // Only works when p is prime
    return fastPower(a, p - 2, p);
}

// Modular division: (a / b) mod p = (a * b^(-1)) mod p
long long modDivision(long long a, long long b, long long p) {
    return (a * modInverse(b, p)) % p;
}

Using Extended Euclidean Algorithm

long long modInverseExt(long long a, long long m) {
    int x, y;
    int g = extendedGCD(a, m, x, y);
    
    if (g != 1) {
        return -1; // Inverse doesn't exist
    }
    
    return (x % m + m) % m;
}

Euler's Totient Function

φ(n) counts the positive integers up to n that are coprime to n.

// Calculate φ(n) for single number
int phi(int n) {
    int result = n;
    
    for (int p = 2; p * p <= n; p++) {
        if (n % p == 0) {
            // Remove all factors of p
            while (n % p == 0) {
                n /= p;
            }
            // Multiply result by (1 - 1/p)
            result -= result / p;
        }
    }
    
    // If n has a prime factor greater than sqrt(n)
    if (n > 1) {
        result -= result / n;
    }
    
    return result;
}

// Compute φ(i) for all i from 1 to n using sieve
vector<int> phiSieve(int n) {
    vector<int> phi(n + 1);
    
    // Initialize phi[i] = i
    for (int i = 1; i <= n; i++) {
        phi[i] = i;
    }
    
    // Apply Euler's formula
    for (int i = 2; i <= n; i++) {
        if (phi[i] == i) { // i is prime
            for (int j = i; j <= n; j += i) {
                phi[j] -= phi[j] / i;
            }
        }
    }
    
    return phi;
}

Euler's Theorem:

If gcd(a, n) = 1, then a^φ(n) ≡ 1 (mod n)

This generalizes Fermat's Little Theorem.

Chinese Remainder Theorem

Solves system of congruences: x ≡ a₁ (mod m₁), x ≡ a₂ (mod m₂), ..., x ≡ aₖ (mod mₖ)

// Solve x ≡ a (mod m) and x ≡ b (mod n) where gcd(m, n) = 1
long long crt(long long a, long long m, long long b, long long n) {
    int x, y;
    int g = extendedGCD(m, n, x, y);
    
    if (g != 1) {
        return -1; // No solution if m and n are not coprime
    }
    
    long long result = ((a * n % (m * n)) * y % (m * n) + 
                       (b * m % (m * n)) * x % (m * n)) % (m * n);
    
    return (result + m * n) % (m * n);
}

// General CRT for multiple congruences
pair<long long, long long> crtGeneral(vector<long long> a, vector<long long> m) {
    int n = a.size();
    long long a1 = a[0], m1 = m[0];
    
    for (int i = 1; i < n; i++) {
        long long a2 = a[i], m2 = m[i];
        int x, y;
        long long g = extendedGCD(m1, m2, x, y);
        
        if ((a2 - a1) % g != 0) {
            return {-1, -1}; // No solution
        }
        
        long long lcm = m1 / g * m2;
        a1 = ((a1 + m1 * ((a2 - a1) / g) % (lcm / m1) * x) % lcm + lcm) % lcm;
        m1 = lcm;
    }
    
    return {a1, m1}; // x ≡ a1 (mod m1)
}

Linear Diophantine Equations

Equation of the form ax + by = c has integer solutions if and only if gcd(a, b) divides c.

bool solveDiophantine(int a, int b, int c, int& x, int& y) {
    int g = extendedGCD(abs(a), abs(b), x, y);
    
    if (c % g != 0) {
        return false; // No solution
    }
    
    // Scale the solution
    x *= c / g;
    y *= c / g;
    
    // Handle negative coefficients
    if (a < 0) x = -x;
    if (b < 0) y = -y;
    
    return true;
}

// Find all solutions in a range
void findAllSolutions(int a, int b, int c, int minx, int maxx) {
    int x, y;
    if (!solveDiophantine(a, b, c, x, y)) {
        cout << "No solution" << endl;
        return;
    }
    
    int g = gcd(abs(a), abs(b));
    int dx = b / g;  // Step for x
    int dy = -a / g; // Step for y
    
    // Find the range of valid k values
    // x + k * dx should be in [minx, maxx]
    int k_min = (minx - x + dx - 1) / dx; // Ceiling division
    int k_max = (maxx - x) / dx;          // Floor division
    
    for (int k = k_min; k <= k_max; k++) {
        int sol_x = x + k * dx;
        int sol_y = y + k * dy;
        cout << "Solution: x = " << sol_x << ", y = " << sol_y << endl;
    }
}

Number Theory Applications

Counting Divisors

int countDivisors(int n) {
    int count = 0;
    for (int i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            if (i * i == n) {
                count++; // Perfect square
            } else {
                count += 2; // i and n/i are both divisors
            }
        }
    }
    return count;
}

// Using prime factorization: if n = p₁^a₁ * p₂^a₂ * ... * pₖ^aₖ
// then number of divisors = (a₁ + 1) * (a₂ + 1) * ... * (aₖ + 1)
int countDivisorsFromFactors(vector<pair<int, int>> factors) {
    int count = 1;
    for (auto [prime, power] : factors) {
        count *= (power + 1);
    }
    return count;
}

Sum of Divisors

long long sumDivisors(int n) {
    long long sum = 0;
    for (int i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            sum += i;
            if (i != n / i) {
                sum += n / i;
            }
        }
    }
    return sum;
}

// Using prime factorization: sum = ∏(pᵢ^(aᵢ+1) - 1)/(pᵢ - 1)
long long sumDivisorsFromFactors(vector<pair<int, int>> factors) {
    long long sum = 1;
    for (auto [p, a] : factors) {
        long long term = (fastPower(p, a + 1, MOD) - 1 + MOD) % MOD;
        term = (term * modInverse(p - 1, MOD)) % MOD;
        sum = (sum * term) % MOD;
    }
    return sum;
}

Practical Tips

Performance Tips:

  • Precompute primes using sieve for multiple queries
  • Use fast exponentiation for large powers
  • Cache results of expensive computations like φ(n)
  • Use long long to avoid overflow in intermediate calculations

Common Pitfalls:

  • Integer overflow in modular multiplication
  • Forgetting to take modulo in intermediate steps
  • Division by zero when computing modular inverse
  • Not checking if modular inverse exists

Key Formulas:

  • φ(p^k) = p^k - p^(k-1) for prime p
  • φ(mn) = φ(m)φ(n) if gcd(m,n) = 1
  • Σφ(d) = n for all divisors d of n
  • a^φ(n) ≡ 1 (mod n) if gcd(a,n) = 1

CodeForces Challenge Problems

Practice number theory concepts:

Medium

Prime Numbers

Practice prime number generation and factorization problems.

Primes Math
Solve Problem