14. Modulo and its theorems — Homework Solutions
The answer .cpp files are in
exercises/14/homework/solutions/.
Open this page after trying on your own. Compare your line of reasoning with the How to think about it section, not the code line by line: two pieces of code with different shapes can both be correct, and what is worth copying is how to get there. The Test part of each problem contains inputs and outputs that have already been checked — run your own answer on those inputs before considering yourself done.
Running one of the answers:
g++ -std=c++17 -O2 -o jawab exercises/14/homework/solutions/01-mod-benar.cpp
./jawab
Problem 1 — Correct modulo
Problem. A remainder that is always between
0 and m − 1, including for negative
a.
How to think about it. a % m in C++ can
be negative. Adding m once is enough to make it positive,
because a % m is never smaller than −(m − 1).
The final % m brings it back into range if the addition
made the value reach m.
Walkthrough.
cout << ((a % m) + m) % m << "\n";Tests.
| Input | Output | Note |
|---|---|---|
7 3 |
1 |
positive number, unchanged |
-7 3 |
2 |
C++ itself answers -1 |
-1 1000000007 |
1000000006 |
a pattern that often appears in modulo problems |
The third row is the form you will meet often: the result of a
modular subtraction that falls to -1 must be brought back
to MOD - 1, not left negative.
Common mistakes.
- Writing
if (a < 0) a = -a;. That changes the problem:−7 mod 3is not7 mod 3. - Adding
mwithout the final% m, soa = 0gives the answerm, not0.
Problem 2 — Factorial modulo
Problem. n! mod 10⁹ + 7 for
n up to one million.
How to think about it. 1,000,000! has more than five million digits — impossible to store. The multiplication property of modulo saves it: take the remainder at every multiplication step, not at the end.
Walkthrough.
long long hasil = 1;
for (int i = 2; i <= n; i++) hasil = hasil * i % MOD;
cout << hasil << "\n";For n = 10 the result is 3,628,800 (still below MOD, so
it appears in full); for n = 1,000,000 the result is
641,102,369.
Tests.
| Input | Output | Note |
|---|---|---|
0 |
1 |
0! = 1 |
5 |
120 |
still below MOD, so it appears as is |
20 |
146326063 |
20! = 2.4 × 10¹⁸, already folded by the modulo |
1000000 |
641102369 |
the limit the problem asks for |
The third row is a good check: 20! still fits in
long long, so you can compute it without modulo, then take
the remainder yourself to compare. From 21! on, that approach can no
longer be used.
Common mistakes.
- Storing
hasilasint.hasil * ioverflows immediately. - Taking the modulo only once at the end. The intermediate values are already corrupted long before that.
- Using
doubleto hold a large factorial.doubleloses precision after about 15 digits.
Problem 3 — Fast modular exponentiation
Problem. aᵇ mod m with b
up to 10¹⁸.
How to think about it. The key to the speedup: each
step halves b, while squaring a. If the lowest
bit of b is set, the current a is also
multiplied into the result.
The line a %= m; at the start matters for a
larger than m.
Walkthrough.
long long pangkat_mod(long long a, long long b, long long m) {
long long hasil = 1;
a %= m;
while (b > 0) {
if (b % 2 == 1) hasil = hasil * a % m;
a = a * a % m;
b /= 2;
}
return hasil;
}Check: 2 10 1000 gives 24 (2¹⁰ = 1024), and
3 0 7 gives 1 — a zero exponent always gives 1.
Tests.
| Input | Output |
|---|---|
2 10 1000 |
24 |
3 0 7 |
1 |
2 1000000000000000000 1000000007 |
719476260 |
The third row is the reason this whole function exists: the exponent is 10¹⁸, and the answer still appears instantly because only about 60 squarings are performed.
Common mistakes.
- Using a loop of
biterations. Forb = 10¹⁸the program will never finish. - Writing
intforaorhasil.a * awithaclose to 10⁹ needslong long. - Forgetting that for
m = 1the answer is always 0; the formula above already handles it becausehasil * a % 1is always 0.
Problem 4 — Divisibility by 9 test
Problem. A number of up to 100,000 digits, divisible by 9 or not.
How to think about it. The number does not fit in
any type, so it is read as a string. The property used: 10
≡ 1 (mod 9), so 10ᵏ ≡ 1 for every k. This means every digit
contributes its own value, and the number is congruent to the
sum of its digits modulo 9.
A digit character is turned into a number with
s[i] - '0'.
Walkthrough.
string s;
cin >> s;
long long jumlah = 0;
for (int i = 0; i < (int)s.size(); i++) jumlah += s[i] - '0';
if (jumlah % 9 == 0) cout << "YA\n";
else cout << "TIDAK\n";Tests.
| Input | Digit sum | Output |
|---|---|---|
7 |
7 | TIDAK |
123456789 |
45 | YA |
999999999999999999999999 |
216 | YA |
The third row is the core of the problem: that 24-digit number does
not fit in long long at all, but its digit sum can be
computed as long as the number is read as a string. This is
the general pattern for "very long number" problems.
Common mistakes.
- Trying
long long n; cin >> n;for a 100,000-digit number. - Adding
s[i]without- '0', so what gets added is the character codes (48 to 57). - Using this rule for divisors other than 3 and 9. For 11 the rule is different (alternating digit sum), because 10 ≡ −1 (mod 11).
Challenge — Modular inverse
Problem. (x / y) mod p with prime
p.
How to think about it. Division does not have the
modulo property. What is used is the inverse: the
number y⁻¹ satisfying y × y⁻¹ ≡ 1 (mod p).
Fermat's little theorem gives it directly:
y⁻¹ ≡ yᵖ⁻² (mod p).
Once the inverse is found, division becomes multiplication:
(x / y) mod p = x × y^(p−2) mod p
The check at the end is a good habit: multiply the result back by
y; if it is correct, the result comes back to
x mod p.
Walkthrough.
long long invers(long long a, long long p) {
return pangkat_mod(a, p - 2, p);
}
long long hasil = x % p * invers(y, p) % p;
cout << hasil << "\n";
cout << hasil * (y % p) % p << "\n";For 10 3 1000000007, the result is 333,333,339 — and
333,333,339 × 3 mod 10⁹ + 7 = 10, as expected.
Tests.
| Input | Output (two lines) | Note |
|---|---|---|
10 3 7 |
1 then 3 |
10/3 mod 7 = 1, because 1 × 3 ≡ 10 (mod 7) |
1 2 1000000007 |
500000004 then 1 |
inverse of 2 modulo 10⁹+7 |
That second check line is the habit worth copying: after dividing modularly, multiply the result back by the denominator and make sure it returns to the original value. A check that short catches almost all modular inverse mistakes.
Common mistakes.
- Using this formula for a
pthat is not prime. Fermat's little theorem does not apply; what is needed is the extended Euclidean algorithm (Chapter 15). - Looking for the inverse of a
ythat is divisible byp. That inverse does not exist, and the formula gives a meaningless answer. - Writing
x / y % pwith ordinary integer division. Forx = 10,y = 3that gives 3 — an entirely different answer.
OSN-style exercises — walkthrough
The answer files are in
exercises/14/osn/solutions/.
Problem 1 — Power Series Remainder
How to think about it. There are N
terms, and each term is a power with an exponent up to 10⁹. Computing
one term by multiplying K times means 10⁶ × 10⁹ = 10¹⁵
multiplications. Fast modular exponentiation brings that down to about
30 multiplications per term, so the total is about 3 × 10⁷ — it fits.
Measured on the author's machine, N = 10⁶ and
K = 10⁹ finishes in 0.18 seconds.
Walkthrough.
const long long MOD = 1000000007LL;
long long pangkat(long long alas, long long eksponen) {
long long hasil = 1;
alas %= MOD;
while (eksponen > 0) {
if (eksponen % 2 == 1) hasil = hasil * alas % MOD;
alas = alas * alas % MOD;
eksponen /= 2;
}
return hasil;
}
// ...
long long total = 0;
for (long long i = 1; i <= n; i++) {
total = (total + pangkat(i, k)) % MOD;
}The line alas %= MOD (alas = "base") at the
start of the function is not decoration: it guarantees the function
stays correct even when called with a base larger than the modulus.
Tests.
| Input | Output | What it checks |
|---|---|---|
3 2 |
14 |
sample test |
10 1 |
55 |
sum of 1 to 10 |
1 1000000000 |
1 |
1 to any power is still 1 |
1000000 1000000000 |
310989488 |
upper limit, also a timing test |
Common mistakes.
- Computing the power with a loop of
Kiterations. - Summing all the terms first and only then taking the remainder. The accumulator overflows long before it finishes.
- Using
intinside the power function, soalas * alasoverflows.
Problem 2 — Tower of Powers
How to think about it. The exponent,
B^C, cannot be computed at all — for
B = C = 10⁹ the number has about nine billion
digits. So what gets reduced is not the result, but the exponent.
Fermat's little theorem states a^(p−1) ≡ 1 (mod p) for
prime p and a not a multiple of
p. As a consequence, powers of a repeat with
period p − 1, so
a^e ≡ a^(e mod (p−1)) (mod p)
So the steps are two fast modular exponentiations with
different moduli: first B^C
mod (p − 1) to get the exponent, then A to
that exponent mod p.
The condition "A is not a multiple of p" is
guaranteed by the constraint A ≤ 10⁹, which is smaller than
p = 1,000,000,007. Without that guarantee, this step is not
valid — and OSN problems that use this technique almost always include a
similar constraint.
Walkthrough.
long long eksponen = pangkat(b, c, MOD - 1);
cout << pangkat(a, eksponen, MOD) << "\n";Since it is used with two moduli, the power function takes the modulus as a parameter.
Tests.
| Input | Output | What it checks |
|---|---|---|
2 3 2 |
512 |
sample test, the result is still small |
5 1 1 |
5 |
exponent equal to 1 |
2 10 9 |
140625001 |
exponent 10⁹, forces the use of Fermat |
Common mistakes.
- Computing
b * cas the exponent. That isA^(B×C), a different problem. - Reducing the exponent modulo
MODinstead ofMOD − 1. - Using
pangkat(b, c, MOD)for the exponent — the moduli are swapped, and the result is wrong without any symptom.
Problem 3 — Subarrays Divisible by K
How to think about it. The sum of the subarray
A_l through A_r equals
pre[r] − pre[l−1], as in Chapter 10. That sum is divisible
by K exactly when pre[r] and
pre[l−1] have the same remainder when
divided by K.
So the problem changes shape: count how many pairs of prefixes have
the same remainder. That can be done in a single pass with a counter
array of length K: when a new remainder is computed, add
the number of earlier prefixes with the same remainder, then record this
prefix.
The empty prefix — before the first element — must also be counted,
because it represents subarrays that start at the first element. That is
why cacah[0] ("count") is set to 1 before the loop.
Negative numbers. % in C++ follows the
sign of the dividend, so the remainder can be negative and immediately
become an out-of-bounds array index. The pattern
((x % k) + k) % k fixes it.
Walkthrough.
vector<long long> cacah(k, 0);
cacah[0] = 1; // empty prefix
long long sisa = 0, hasil = 0;
for (int i = 0; i < n; i++) {
long long x;
cin >> x;
sisa = ((sisa + x) % k + k) % k; // stays non-negative
hasil += cacah[sisa];
cacah[sisa]++;
}Notice that sisa ("remainder") never stores the actual
sum, only its remainder. The actual sum can reach 2 × 10¹⁴ and is not
needed.
The number of subarrays in the worst case — 200,000 numbers that are
all multiples of K — reaches about 2 × 10¹⁰, so
hasil must be long long.
Tests.
| Input | Output | What it checks |
|---|---|---|
5 3 / 1 2 3 4 1 |
4 |
sample test |
4 2 / 2 4 6 8 |
10 |
every subarray qualifies |
3 5 / -5 5 -5 |
6 |
negative numbers |
The second case is the check for cacah[0] = 1: with four
numbers, all 4 × 5 / 2 = 10 subarrays are divisible by 2. An answer of
6 means the empty prefix was not counted.
Common mistakes.
- Forgetting
cacah[0] = 1, so all subarrays starting at the first element are not counted. - Using
sisa = (sisa + x) % kwithout the sign adjustment, so the program readscacah[-2]and ends in a Runtime Error or a random answer. - Storing the actual prefix sums and then comparing them pairwise.
Correct, but
N²= 4 × 10¹⁰.
If you are still stuck
- The modulo result becomes negative → Chapter 14, section
Remainder and the function
mod_benar. - The answer overflows even though it is already reduced → check the
data type: the product of two numbers below 10⁹ + 7 must be computed as
long long. - Modular division gives a nonsensical number → Chapter 14, section Fermat's little theorem; division is done with the inverse.
The two tools of this chapter — fast modular exponentiation and the modular inverse — are used again in Chapter 17 to compute combinations.