No history yet

Mathematical Foundations

Working with Big Numbers

In competitive programming, you'll often face problems where the answers are enormous. Standard 64-bit integers in C++ can hold values up to about 9×10189 \times 10^{18}, but calculations can easily exceed this limit, causing an integer overflow. This is where modular arithmetic comes in. It's a system for dealing with numbers that wrap around after reaching a certain value, called the modulus. Think of a clock: hours wrap around after 12. In programming, we often use a large prime number as the modulus, like 109+710^9 + 7. This specific number is large enough to avoid most accidental collisions but small enough to fit within a 64-bit integer when squared.

The core idea is that instead of computing a final massive result and then finding its remainder, we apply the modulo at each step of the calculation. This keeps the intermediate numbers manageable.

(a+b)(modm)=((a(modm))+(b(modm)))(modm)(ab)(modm)=((a(modm))(b(modm)))(modm)\begin{aligned} (a + b) \pmod{m} &= ((a \pmod{m}) + (b \pmod{m})) \pmod{m} \\ (a \cdot b) \pmod{m} &= ((a \pmod{m}) \cdot (b \pmod{m})) \pmod{m} \end{aligned}

Division is trickier. We can't just divide and take the modulus. Instead, we multiply by a modular multiplicative inverse. For a number aa, its modular inverse a1a^{-1} (modulo mm) is a number such that (aa1)(modm)=1(a \cdot a^{-1}) \pmod{m} = 1. This inverse only exists if aa and mm are coprime, which means their greatest common divisor is 1. Since we often use a prime modulus like 109+710^9 + 7, this condition is almost always met.

Finding Common Ground

The Greatest Common Divisor (GCD) of two numbers is the largest number that divides both of them without leaving a remainder. A naive approach would be to check every number from 1 up to the smaller of the two numbers, but that's too slow. The Euclidean algorithm provides a much faster, logarithmic-time solution. It's based on the principle that the GCD of two numbers does not change if the larger number is replaced by its difference with the smaller number. More efficiently, we can use the remainder.

Lesson image

The algorithm states that gcd(a,b)=gcd(b,a%b)gcd(a, b) = gcd(b, a \% b), where a%ba \% b is the remainder when aa is divided by bb. We repeat this process until the second number becomes 0. The first number is then the GCD. Once you have the GCD, finding the Least Common Multiple (LCM) is easy.

lcm(a,b)=abgcd(a,b)lcm(a, b) = \frac{|a \cdot b|}{gcd(a, b)}
// C++ implementation of Euclidean algorithm
long long gcd(long long a, long long b) {
    while (b) {
        a %= b;
        std::swap(a, b);
    }
    return a;
}

// Function to calculate LCM
long long lcm(long long a, long long b) {
    return (a / gcd(a, b)) * b;
}

Efficient Operations

What if you need to compute ab(modm)a^b \pmod{m} where bb is very large? A simple loop that multiplies aa by itself bb times would be far too slow. We can do much better using a technique called binary exponentiation (also known as exponentiation by squaring). This algorithm works in logarithmic time, O(logb)O(\log b). The idea is to use the binary representation of the exponent bb. For example, to calculate 3103^{10}, we note that 1010 in binary is 101021010_2. This means 10=8+210 = 8 + 2, so 310=38323^{10} = 3^8 \cdot 3^2. We can compute the powers 31,32,34,38,...3^1, 3^2, 3^4, 3^8, ... by repeatedly squaring the previous result.

// C++ implementation of binary exponentiation
long long power(long long base, long long exp) {
    long long res = 1;
    base %= 1000000007; // Modulo
    while (exp > 0) {
        // If exp is odd, multiply base with result
        if (exp % 2 == 1) res = (res * base) % 1000000007;
        // exp must be even now
        base = (base * base) % 1000000007;
        exp /= 2;
    }
    return res;
}

A related concept is Euler's totient function, ϕ(n)\phi(n). This function counts the number of positive integers up to a given integer nn that are relatively prime to nn (meaning their GCD with nn is 1). Euler's theorem states that if aa and nn are relatively prime, then aϕ(n)1(modn)a^{\phi(n)} \equiv 1 \pmod{n}. This is a generalization of Fermat's Little Theorem and is particularly useful for finding modular inverses and reducing large powers. For a prime number pp, ϕ(p)=p1\phi(p) = p-1.

Counting without Counting

Combinatorics is the art of counting arrangements of objects. A common problem is calculating "n choose r", or nCrnC_r, which is the number of ways to choose rr items from a set of nn items without regard to order. The formula is:

nCr=(nr)=n!r!(nr)!nC_r = \binom{n}{r} = \frac{n!}{r!(n-r)!}

A more specific and fascinating counting problem involves Catalan numbers They appear in a surprising number of scenarios, such as: finding the number of correctly matched bracket sequences of length 2n2n, or the number of ways a convex polygon with n+2n+2 sides can be cut into triangles by connecting vertices. The formula for the nn-th Catalan number, CnC_n, is:

Cn=1n+1(2nn)=(2n)!(n+1)!n!C_n = \frac{1}{n+1} \binom{2n}{n} = \frac{(2n)!}{(n+1)!n!}

These mathematical tools are shortcuts. They turn problems that seem to require complex loops or recursion into elegant, efficient calculations. Mastering them is a key step in leveling up your competitive programming skills.