number theory
Number Theory
Divisors
For example the divisors of a number like 12 (call it n) will be [1, 2, 3, 4, 6, 12].
If we look at them in pairs:
[1, 12]
[2, 6]
[3, 4]
We could just get the first three [1, 2, 3] — call them x — and [4, 6, 12] is y, so x * y = n. By getting x we could get y because we know n.
That saves time: while searching for divisors of n we only go up to √n. In our case (int)√12 = 3, and we get y by dividing n / x.
This runs in O(√n) instead of O(n).
vector<long long> divisors(long long n) {
vector<long long> ret;
for (long long i = 1; i * i <= n; i++) {
if (n % i == 0) {
ret.push_back(i);
if (i * i != n) {
ret.push_back(n / i);
}
}
}
return ret;
}
For 12 we get [1, 12, 2, 6, 3, 4] — unsorted, but that’s fine.
The big deal comes with perfect squares like 16: without the i * i != n check we’d get [1, 16, 2, 8, 4, 4] with a duplicate 4.
This algorithm is efficient for finding divisors of one specific number.
But if we want divisors for every number from 1 to n, calling this for each number takes O(n√n). With n queries that’s O(n²√n) — TLE.
So we preprocess all divisors using a divisor sieve. It works on the fact: if i divides j, then i is a divisor of j. So for each i, we add it to all its multiples.
vector<vector<ll>> sieve(ll x) {
vector<vector<ll>> ret(x + 1);
for (int i = 1; i <= x; i++) {
for (int j = i; j <= x; j += i) {
ret[j].push_back(i);
}
}
return ret;
}
Preprocessing: O(n log n). Each query: O(1) lookup.
Problems using this
A. k-th divisor— get all divisors, sort, return k-thA. Divisors Queries— preprocess once, answer many queriesR. Easy Number Challenge— uses divisor count sieve (see below)
Divisor Count
Same sieve idea, but instead of storing the list we just count:
vi divisors_cnt(ll x) {
vi ret(x + 1, 0);
for (int i = 1; i <= x; i++) {
for (int j = i; j <= x; j += i) {
ret[j]++;
}
}
return ret;
}
ret[n] = number of divisors of n.
Problems using this
R. Easy Number Challenge— sum of divisor counts over a 3D range
Prime Numbers
Divisors of 13 are [1, 13] — exactly two divisors → prime.
Divisors of 12 are [1, 2, 3, 4, 6, 12] — more than two → not prime.
Checking one number with the divisor function works fine.
But checking every number from 1 to n with that approach is O(n√n) — too slow.
Preprocess with the Sieve of Eratosthenes: assume every number is prime, then mark multiples of each prime as not prime.
vector<bool> sieve(ll n) {
vector<bool> is_prime(n + 1, true);
is_prime[0] = is_prime[1] = false;
for (ll i = 2; i * i <= n; i++) {
if (is_prime[i]) {
for (ll j = i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return is_prime;
}
Preprocessing: O(n log log n). Each query: O(1).
Problems using this
B - Is it Prime— single prime checkQ. T-primes— extended below
T-primes
A T-prime is a number that has exactly 3 divisors.
When does a number have exactly 3 divisors? Only when it is the square of a prime:
- Divisors of
p²are[1, p, p²]→ exactly 3.
So to check if x is a T-prime:
- Check if
xis a perfect square. - Check if
√xis prime (using precomputed sieve).
vector<bool> ret = sieve(1000005);
void solve() {
ll x; cin >> x;
if ((double)sqrt(x) - (ll)sqrt(x) == 0) {
if (ret[sqrt(x)]) cout << "YES" << el;
else cout << "NO" << el;
} else {
cout << "NO" << el;
}
}
Problems using this
Q. T-primes
Prime Factorization (SPF Sieve)
Prime factorization of 60 is 2 × 2 × 3 × 5.
One way: repeatedly divide by the smallest prime divisor.
The basic Eratosthenes sieve only tells us if a number is prime, not its smallest divisor. While building the sieve, store the smallest prime divisor (SPF) for each number.
vi sieve(ll n) {
vi divided(n + 1, 0);
vector<bool> is_prime(n + 1, true);
is_prime[0] = is_prime[1] = false;
for (ll i = 2; i <= n; i++) {
if (is_prime[i]) {
divided[i] = i;
for (ll p = i * i; p <= n; p += i) {
is_prime[p] = false;
if (divided[p] == 0) {
divided[p] = i;
}
}
}
}
return divided;
}
Then factorize by dividing repeatedly:
vi prime_factorization(ll n, vi ÷d) {
vi ret;
while (n != 1) {
ll p = divided[n];
ret.push_back(p);
n /= p;
}
return ret;
}
For n = 60: 60 → 2, 30 → 2, 15 → 3, 5 → 5, 1 → [2, 2, 3, 5].
Preprocessing: O(n log log n). Each query: O(log n).
Problems using this
G. Almost Prime— count numbers with exactly 2 distinct prime factorsP. k-Factorization— output first k-1 prime factors, multiply rest
Almost Primes
A number is almost prime if it has exactly 2 distinct prime factors (multiplicity doesn’t matter).
Example: 6 = 2 × 3 → almost prime. 12 = 2 × 2 × 3 → still almost prime (distinct: {2, 3}).
After factorization, count distinct primes:
vi f = prime_factorization(i, d);
int cnt = 0;
for (int j = 0; j < f.size(); j++) {
if (j == 0 || f[j] != f[j - 1]) cnt++;
}
if (cnt == 2) ans++;
Problems using this
G. Almost Prime
Divisors of a Product
If n = p1^e1 × p2^e2 × ..., the number of divisors is (e1+1)(e2+1)....
For a product of many numbers, factor each one and combine exponents in a frequency map:
map<ll, ll> freq;
for (each number x in the array) {
while (x > 1) {
freq[spd[x]]++;
x /= spd[x];
}
}
ll ans = 1;
for (auto [pf, frq] : freq) {
ans *= (frq + 1);
ans %= MOD;
}
For large limits, use a linear sieve (faster SPF):
void sieve() {
for (ll i = 2; i <= N; i++) {
if (spd[i] == 0) {
spd[i] = i;
pr.push_back(i);
}
for (ll j = 0; j < pr.size() && pr[j] <= spd[i] && i * pr[j] <= N; j++) {
spd[i * pr[j]] = pr[j];
}
}
}
Problems using this
B. Divisors of Product
GCD Tricks
GCD of an array after adding x to every element
If we add x to every element: [a1+x, a2+x, ..., an+x].
Key identity: gcd(a, b) = gcd(a, b - a).
So gcd(a1+x, a2+x, ..., an+x) = gcd(a2-a1, a3-a1, ..., an-a1, a1+x).
Sort the array, compute GCD of all differences from a[0], then for each query x answer gcd(gc, x + a[0]).
sort(a.begin(), a.end());
ll gc = 0;
for (int i = 1; i < n; i++) {
gc = gcd(gc, a[i] - a[0]);
}
// query x:
cout << gcd(gc, x + a[0]) << " ";
Problems using this
C. Row GCD
GCD Arrays
Question: can we make every adjacent pair have GCD > 1 by deleting at most k elements?
Observation: if two numbers are both odd, their GCD is at least 1 but could still be 1 (e.g. 3 and 5). If at least one is even, GCD ≥ 2.
Count evens in [l, r]. The “bad” numbers are odds that can’t pair with an even. If remaining_odds <= k, answer is YES.
ll good = (r / 2) - ((l - 1) / 2); // count of evens
ll remain = (r - l + 1) - good; // count of odds
if (remain <= k) cout << "YES";
Edge case: l == r and l > 1 → YES (single element, no adjacent pair).
Problems using this
X. GCD Arrays
Maximum GCD
For a set {1, 2, ..., n}, what’s the maximum GCD of any pair?
Best pair: (n/2, n) when n is even → GCD = n/2.
If n is odd, use (n-1, (n-1)/2) → GCD = (n-1)/2.
if (n % 2 != 0) n--;
cout << gcd(n, n / 2);
Problems using this
Y. Maximum GCD
LCM (Least Common Multiple)
The LCM of two numbers is the smallest number that both divide.
Example: multiples of 4 are 4, 8, 12, 16... and multiples of 6 are 6, 12, 18.... The first common multiple is 12, so lcm(4, 6) = 12.
LCM and GCD
There is a direct link between GCD and LCM:
lcm(a, b) × gcd(a, b) = a × b
So:
lcm(a, b) = a / gcd(a, b) × b
We divide by GCD first to avoid overflow when a and b are large.
C++ has a built-in function in <numeric>:
#include <numeric>
ll lcm_ab = lcm(a, b);
// same as:
ll lcm_ab = a / gcd(a, b) * b;
LCM of Three Numbers
LCM is associative — compute two at a time:
ll lcm3(ll a, ll b, ll c) {
return lcm(lcm(a, b), c);
}
Example: lcm(4, 6, 10)
lcm(4, 6) = 12
lcm(12, 10) = 60
LCM Challenge — Maximum LCM of Three Numbers
Problem: Given n, find the maximum value of lcm(i, j, k) where 1 ≤ i ≤ j ≤ k ≤ n.
The straightforward approach tries every triple:
for (ll i = 1; i <= n; i++)
for (ll j = i; j <= n; j++)
for (ll k = j; k <= n; k++)
ans = max(ans, lcm3(i, j, k));
This is O(n³) — fine for small n, but TLE when n is up to 10⁶.
But we can prove the answer always comes from the last 5 numbers near n.
Why only check [n-4, n]?
lcm(i, j, k) ≥ max(i, j, k) = k— the LCM is at least the largest number in the triple.- So to maximize LCM, we want large numbers.
- It can be shown that for any optimal triple, we can replace small values with numbers from
{n-4, n-3, n-2, n-1, n}without getting a smaller LCM. - Any number
≤ n-5is too far fromnto beat a triple built from the top 5 values.
So instead of checking all O(n³) triples, we only check triples from the last 5 numbers — at most C(5+2, 3) = 35 triples (with i ≤ j ≤ k). That’s O(1).
ll ans = 0;
ll start = max(1LL, n - 4);
for (ll i = start; i <= n; i++) {
for (ll j = i; j <= n; j++) {
for (ll k = j; k <= n; k++) {
ans = max(ans, lcm3(i, j, k));
}
}
}
cout << ans;
max(1, n-4) handles small n (when n < 5, we check from 1).
Examples
| n | Best triple | Answer |
|---|---|---|
| 3 | (1, 2, 3) | lcm = 6 |
| 7 | (5, 6, 7) | lcm = 210 |
| 10 | (7, 8, 9) | lcm = 504 |
Notice how the best triple uses numbers right below n, not {1, 2, n}.
Problems using this
E. LCM Challenge
LCM in Counting (Inclusion–Exclusion)
When counting multiples in [1, n]:
- Multiples of
x:n / x - Multiples of
y:n / y - Multiples of both
xandy:n / lcm(x, y)(because a number divisible by both is divisible by their LCM)
ll cntX = n / x;
ll cntY = n / y;
ll cntBoth = n / lcm(x, y);
ll onlyX = cntX - cntBoth;
ll onlyY = cntY - cntBoth;
This is inclusion–exclusion: numbers divisible by both were counted twice, so subtract cntBoth once from each side.
Problems using this
D. Plus Minus Permutation— sum of positions divisible by x minus sum divisible by y
Divisor Subtraction
Given n, repeatedly subtract its smallest divisor > 1. How many steps to reach 1?
- If
nis even: smallest divisor is 2 → each step removes 2 →n/2steps. - If
nis odd prime: can’t subtract anything > 1 properly → answer is 1. - If
nis odd composite: subtract smallest divisord, then continue on the result.
if (n % 2 == 0) {
cout << n / 2;
} else {
ll d = n;
for (ll i = 3; i * i <= n; i++) {
if (n % i == 0) { d = i; break; }
}
if (d == n) cout << 1; // prime
else cout << 1 + (n - d) / 2;
}
Problems using this
N. Divisor Subtraction
Different Divisors
Find the smallest number ≥ d+1 whose divisors are all different (i.e. a product of two distinct primes with gap ≥ d).
Find p = next_prime(d+1), then q = next_prime(p+d), answer = p * q.
Problems using this
W. Different Divisors
Big Number Modulo
When n is a huge number stored as a string, we can’t convert it to long long. Compute n mod b digit by digit:
Process digits from right to left (or left to right with Horner’s method):
string s; cin >> s;
ll b; cin >> b;
reverse(s.begin(), s.end());
ll ans = 0, tenPowX = 1;
for (ll i = 0; i < s.size(); i++) {
ll digit = s[i] - '0';
ans = (ans + digit % b * tenPowX % b) % b;
tenPowX = tenPowX * 10 % b;
}
Problems using this
C. Remainder Quest
Some Important Identities
Sum of first n natural numbers
$$ 1 + 2 + 3 + \dots + n = \frac{n(n+1)}{2} $$
Sum of first n squares
$$ 1^2 + 2^2 + 3^2 + \dots + n^2 = \frac{n(n+1)(2n+1)}{6} $$
Sum of first n cubes
$$ 1^3 + 2^3 + 3^3 + \dots + n^3 = \left(\frac{n(n+1)}{2}\right)^2 $$
Sum of first n fourth powers
$$ 1^4 + 2^4 + 3^4 + \dots + n^4 = \frac{n(n+1)(2n+1)(3n^2 + 3n - 1)}{30} $$
Fermat’s Little Theorem
Take a prime like 5, and any base not divisible by it — say 2. Look at 2^4:
2^4 = 16
16 mod 5 = 1
Try 3^4 and 4^4 too:
3^4 = 81 → 81 mod 5 = 1
4^4 = 256 → 256 mod 5 = 1
All three land on the same remainder, 1. That’s not a coincidence — it’s guaranteed by Fermat’s Little Theorem: for a prime p and any integer a not divisible by p,
a^(p-1) ≡ 1 (mod p)
(There’s an equivalent form, a^p ≡ a (mod p), that works even when a is a multiple of p. Divide both sides by a to get back the first form.)
Here ≡ (mod p) just means “same remainder when divided by p” — a ≡ b (mod p) iff p divides (a - b). It’s not saying the two sides are equal as numbers, just that they’re indistinguishable once you only care about remainders mod p. That’s what lets us swap a^(p-1) for 1 in the middle of a bigger calculation without changing the final answer mod p.
Why it matters: exponents repeat every p-1 steps
Because a^(p-1) ≡ 1 (mod p), the sequence a^0, a^1, a^2, ... mod p cycles with period p-1 — after every p-1 powers, you’re back where you started. That means if the exponent n is huge (too big to even store), we don’t need the real value of n at all — only how far it sits past the last full cycle.
Split n using ordinary division: n = (p-1)·q + r, so r = n mod (p-1). Then:
a^n = a^((p-1)q + r) = (a^(p-1))^q · a^r ≡ 1^q · a^r = a^r (mod p)
So a^n mod p only depends on r = n mod (p-1) — the q full cycles collapse to 1 and drop out entirely.
But n itself might be given as a giant string (thousands of digits, too big for any integer type). No problem — get n mod (p-1) the same way as in Big Number Modulo above, processing digits one at a time.
ll modP1 = p - 1;
ll r = 0;
for (char c : n_string) {
r = (r * 10 + (c - '0')) % modP1;
}
// now compute a^r mod p directly — r is small
Once r is small, a^r mod p is trivial (loop it out, or binary exponentiation if r is still largish).
Example (p = 5)
| a | a^(p-1) | mod p |
|---|---|---|
| 1 | 1^4 = 1 | 1 |
| 2 | 2^4 = 16 | 1 |
| 3 | 3^4 = 81 | 1 |
| 4 | 4^4 = 256 | 1 |
All four confirm a^4 ≡ 1 (mod 5), which is exactly what lets a huge exponent n collapse down to just n mod 4.
(For a composite modulus instead of a prime, the same idea generalizes to Euler’s theorem, a^φ(m) ≡ 1 (mod m), using φ(m) in place of p-1.)
Problems using this
456B. Fedya and Maths— compute(1^n + 2^n + 3^n + 4^n) mod 5for a hugen; reduces to checkingn mod 4
Divisibility Rules
Take 405. Digit sum = 4+0+5 = 9, divisible by 3. Check directly: 405 / 3 = 135. ✓ Matches.
Rearrange the same digits: 540. Digit sum is still 9 — rearranging never changes the digit sum, so it never changes divisibility by 3. That’s the whole idea behind digit-sum rules: they only depend on which digits you have, not their order.
Not every rule works like that though — some depend on the last few digits instead. Full table:
| Divisor | Rule | Example |
|---|---|---|
| 1 | Always true | any number |
| 2 | Last digit is even (0,2,4,6,8) | 1294 → 4 is even ✓ |
| 3 | Digit sum divisible by 3 | 636 → 6+3+6=15 ✓ |
| 4 | Last two digits form a number divisible by 4 | 456832960 → last two 60, 60/4=15 ✓ |
| 5 | Last digit is 0 or 5 | 500985 ✓ |
| 6 | Divisible by both 2 and 3 | 10008 → last digit 8 (even), sum=9 (÷3) ✓ |
| 8 | Last three digits divisible by 8 | check the last 3 digits directly |
| 9 | Digit sum divisible by 9 | 4725 → 4+7+2+5=18 ✓ |
| 10 | Last digit is 0 | 3456780 ✓ |
| 11 | (sum of digits at odd positions) − (sum at even positions) is 0 or divisible by 11 | 1331 → 1+3=4, 3+1=4, diff=0 ✓ |
| 20 | Last digit 0, second-to-last digit even | 340 → ends 40, 4 is even ✓ |
| 25 | Last two digits are 00, 25, 50, 75 |
2675 → ends 75 ✓ |
Two families, and why the split matters
- Digit-sum rules (3, 9, and 11’s alternating-sum variant): order-independent — depend only on the digit multiset. Trivial to check even for huge numbers given as a string (sum the digits, or process with the running-remainder trick from Big Number Modulo).
- Suffix rules (2, 4, 5, 8, 10, 20, 25, …): depend only on the last
jdigits (j=1for 2/5/10,j=2for 4/20/25,j=3for 8, …). These only matter for the value of the number as written — but if you’re allowed to rearrange digits freely, you instead get to choose what goes in those lastjslots.
Combining rules for a composite divisor — coprime factors only
To check divisibility by some k, split k into pairwise coprime factors (gcd of any two pieces must be 1) and require all pieces to hold at once. This works because coprime divisibility conditions are independent.
Example: k = 60 = 2² × 3 × 5. Valid coprime factor pairs multiplying to 60:
(1, 60), (3, 20), (4, 15), (5, 12)
Something like (6, 10) is not valid — gcd(6,10) = 2 ≠ 1, so “divisible by 6 and by 10” doesn’t imply “divisible by 60” (counterexample: 30 passes both but isn’t divisible by 60).
Among the valid pairs, prefer whichever splits k into pieces that are each easy to check:
(3, 20)→ digit-sum check (3) + suffix-existence check (20: need a0for the last slot, and an even digit — possibly another0— for the slot before it). Both reduce to counting digit frequencies, no need to test actual permutations.(4, 15)or(5, 12)would work too, but checking divisibility by 4 needs the actual value of a 2-digit suffix (not just “any two even digits” — e.g.24works,26doesn’t), which is messier to reason about with rearrangement.
Problems using this
M. Competitive Programmer(CF 1266A) — can the digits ofy_ibe rearranged into a multiple of 60? Split60 = 3 × 20; check digit sum mod 3, and count zeros/evens for a validX0ending.