16. The sum and product rules — Homework Solutions
Problem 1 is worked on paper. The .cpp files for problem
2 through the challenge are in
exercises/16/homework/solutions/.
Open this page after trying on your own. For the paper problem, write down your answer first, then compare — reading someone else's answer feels clear even when you may not be able to repeat it yourself. The Tests part contains input and output that have already been checked for the coding problems.
Running one of the answers:
g++ -std=c++17 -O2 -o jawab exercises/16/homework/solutions/02-periksa.cpp
./jawab
Problem 1 — Count on paper
a. Three-digit numbers with all even digits → 100.
The hundreds digit may not be zero, so the choices are
{2, 4, 6, 8} — four ways. The tens and units digits may be
zero, so {0, 2, 4, 6, 8} — five ways each. Product rule: 4
× 5 × 5 = 100.
b. Three-digit numbers with at least one digit 7 → 252.
Via the complement. There are 900 three-digit numbers in total. Those that do not contain 7 at all: the hundreds digit has 8 choices (1–9 without 7), the tens and units digits 9 each (0–9 without 7) → 8 × 9 × 9 = 648. The answer is 900 − 648 = 252.
c. From 1 to 1,000, divisible by 4 or 6 → 333.
⌊1000/4⌋ = 250, ⌊1000/6⌋ = 166, and the intersection is the multiples of LCM(4, 6) = 12: ⌊1000/12⌋ = 83. So 250 + 166 − 83 = 333.
Note that the intersection uses the LCM, not the product 4 × 6 = 24. Using 24 gives the wrong answer 375.
d. Menu packages → 36.
One food (4 ways) and one drink (3 ways) are required. The dessert has 3 states: take the first one, take the second one, or take none. So 4 × 3 × 3 = 36.
This combines both rules: "may or may not be taken" is the sum rule (2 + 1), which then goes into the product.
Problem 2 — Check with a program
How to think about it. For each part, write a loop that actually checks all 900 numbers (or all menu combinations). If the formula and the enumeration differ, it is almost always the formula that is wrong.
Walkthrough.
int b = 0;
for (int n = 100; n <= 999; n++) {
int r = n / 100, p = (n / 10) % 10, s = n % 10;
if (r == 7 || p == 7 || s == 7) b++;
}
cout << b << " " << 900 - 8 * 9 * 9 << "\n"; // 252 252Tests. The answer program prints both numbers side by side:
a: 100 (rumus 4*5*5 = 100)
b: 252 (rumus 900 - 8*9*9 = 252)
c: 333 (rumus 250 + 166 - 83 = 333)
d: 36 (rumus 4*3*3 = 36)
If one of the lines does not match, it is not necessarily the formula
that is wrong — the direct enumeration can also be wrong, for example
because the loop range is 100 to 1000 instead
of 100 to 999. The right habit: look for the
difference on the smallest case, instead of immediately blaming one
side.
Common mistakes.
- In part (d), counting the dessert as 2 choices, so the answer becomes 24. The phrase "may or may not be taken" adds one more state.
- Writing an enumeration that uses the same formula as the paper answer. The enumeration must be plain — check one by one.
Problem 3 — Passwords
Problem. Passwords of length n from
k letters, at least one letter a, modulo 10⁹ +
7.
How to think about it. Complement: all passwords
kⁿ, those without the letter a number
(k−1)ⁿ. The answer is the difference. Since n
goes up to 10¹⁸, both are computed with fast modular exponentiation
(Chapter 14).
One modulo trap: the result of the subtraction can be negative after
taking the modulo. Normalize it by adding MOD once
more.
Walkthrough.
long long semua = pangkat_mod(k, n, MOD);
long long tanpa_a = pangkat_mod(k - 1, n, MOD);
cout << ((semua - tanpa_a) % MOD + MOD) % MOD << "\n";Check with 4 26: the result is 66,351, the same as the
direct enumeration in the chapter example.
Tests.
| Input | Output | Note |
|---|---|---|
1 1 |
1 |
one letter, and that letter is a |
4 26 |
66351 |
same as the example in Chapter 16 |
1000000000000000000 26 |
878075956 |
already in modular remainder form |
The first row is worth testing because the complement formula there
gives 1 − 0: if there is only one letter and that letter is
a, there is no password "without a". The last
row tests fast modular exponentiation — with an ordinary loop, the
program would never finish.
Common mistakes.
- Printing
semua - tanpa_a("all minus withouta") without normalizing the negative sign. - Computing
kⁿwith a loop ofniterations. - Forgetting the case
k = 1: every password contains only the lettera, and(k−1)ⁿ = 0— the formula is still correct.
Problem 4 — Inclusion-exclusion
Problem. From 1 to n (≤ 10¹⁸), those
divisible by p or q.
How to think about it. The number of multiples of
p up to n is n / p (integer
division). The intersection is the multiples of LCM(p, q).
The big trap: the LCM of two numbers close to 10¹⁸ far exceeds the
long long limit. Yet if the LCM alone is already larger
than n, the intersection term is zero — its exact value is
not needed. So first check whether the multiplication would exceed
n:
Walkthrough.
long long calon = p / fpb(p, q);
long long jawab = n / p + n / q;
if (calon <= n / q) { // calon * q will not exceed n
long long k = calon * q;
jawab -= n / k;
}
cout << jawab << "\n";Check: 100 3 5 gives 47; 1000 4 6 gives
333.
Tests.
| Input | Output | Note |
|---|---|---|
100 3 5 |
47 |
example in Chapter 16 |
1000 4 6 |
333 |
same as problem 1c |
1000000000000000000 2 3 |
666666666666666667 |
tests overflow |
The second row is a good cross-check: the answer must equal your hand
computation in problem 1c. The third row is the reason the LCM is
computed as p / fpb(p, q) * q: with p * q
first, numbers that large already overflow before they can be used.
Common mistakes.
- Using
p * qas the intersection instead of the LCM. Wrong unlesspandqare coprime. - Computing the LCM without an overflow guard, then getting a negative
number that makes
n / kbehave strangely. - Enumerating one by one with a loop up to
n. For 10¹⁸, impossible.
Challenge — Three sets
Problem. Divisible by at least one of three numbers.
The formula (seven terms).
n/p + n/q + n/r
− n/LCM(p,q) − n/LCM(p,r) − n/LCM(q,r)
+ n/LCM(p,q,r)
The pattern: single terms are added, pair terms are subtracted,
triple terms are added again. For m sets there are 2ᵐ − 1
terms, with the sign alternating according to the number of sets
involved.
How to think about it. Every LCM is computed with
the same guard as in problem 4. The triple LCM is computed step by step:
LCM(LCM(p, q), r) — and if the pair LCM alone already exceeds
n, the term can be skipped.
Walkthrough. See 05-tiga-himpunan.cpp.
The checking part:
if (n <= 10000) {
long long cacah = 0;
for (long long i = 1; i <= n; i++)
if (i % p == 0 || i % q == 0 || i % r == 0) cacah++;
cout << "cacah langsung: " << cacah << "\n";
}Check: 100 2 3 5 gives 74 from the formula and 74 from
the enumeration; 10000 4 6 15 gives 3,666 from both.
Tests. The answer program prints the formula and the direct enumeration together:
| Input | Output |
|---|---|
100 2 3 5 |
74 and 74 |
1000 4 6 10 |
366 and 366 |
The second case deliberately uses three numbers that are
not coprime: the intersection of 4 and 6 is the
multiples of 12 (not 24), the intersection of 4 and 10 is the multiples
of 20 (not 40), and the intersection of all three is the multiples of
60. A formula that uses the product instead of the LCM will miss here,
while on 100 2 3 5 it happens to be correct because all
three are coprime.
Common mistakes.
- Swapping the sign on the triple term. If
+is replaced with−, the numbers divisible by all three are not counted at all. - Computing the triple LCM as
p * q * r. - Forgetting to test the formula with direct enumeration. A seven-term
formula is easy to mistype, and enumeration for
n ≤ 10,000catches it within a second.
OSN-style exercises — walkthrough
The answer files are in
exercises/16/osn/solutions/.
Problem 1 — Colored Flag
How to think about it. Color from left to right. The
first stripe has no left neighbor, so it is free: K
choices. Every following stripe is forbidden only one color, namely the
color of its left neighbor — whatever that color is, there is always
exactly one forbidden, so there are always K − 1
choices.
Since every decision stands on its own once the previous decision is fixed, the product rule applies:
K × (K − 1) × (K − 1) × … = K × (K − 1)^(N−1)
An exponent as large as 10¹⁸ is computed with fast modular exponentiation from Chapter 14.
Walkthrough.
long long hasil = k % MOD * pangkat(k - 1, n - 1) % MOD;
cout << hasil << "\n";Edge cases. The formula is already correct for both without an extra branch:
N= 1: the power becomes(K − 1)^0= 1, so the answer isK.K= 1 andN≥ 2: the power is0^(N−1)= 0, so the answer is 0 — indeed there is no valid flag, because two adjacent stripes are necessarily the same color.
Tests.
| Input | Output | What it checks |
|---|---|---|
3 3 |
12 |
the sample |
1 5 |
5 |
one stripe |
5 1 |
0 |
one color, impossible |
1000000000000000000 1000000000 |
707166314 |
upper bound |
For 3 3, the answer can be enumerated by hand or with an
enumerating program — and it is indeed 12.
Common mistakes.
- Writing
K^N, forgetting the neighbor restriction. - Writing
K × (K − 1)^N, with the wrong exponent. - Forgetting
k % MODbefore multiplying. ForK≤ 10⁹ the result happens to still be correct, but the habit of reducing every factor is safer.
Problem 2 — Password With Two Required Letters
How to think about it. Counting "contains
a and contains b" directly is hard. Count the
complement.
Let S be the set of all words of length N,
of size K^N. Let A be the set of words that do
not contain a, of size
(K − 1)^N because every position has K − 1
choices. Let B be the set of words that do not contain
b, also of size (K − 1)^N.
The invalid words are members of A or members of
B. By inclusion-exclusion:
|A ∪ B| = |A| + |B| − |A ∩ B| = 2(K−1)^N − (K−2)^N
The intersection A ∩ B is the words that contain neither
a nor b, of size (K − 2)^N. So
the answer is:
K^N − 2(K−1)^N + (K−2)^N
Walkthrough.
long long semua = pangkat(k, n);
long long tanpaSatu = pangkat(k - 1, n);
long long tanpaKeduanya = pangkat(k - 2, n);
long long hasil = ((semua - 2 * tanpaSatu % MOD) % MOD + tanpaKeduanya) % MOD;
hasil = (hasil % MOD + MOD) % MOD;The last line is what keeps the result from ever being negative. Without it, the program can print a negative number for completely valid input — a symptom that does not show up on the sample.
Tests.
| Input | Output | What it checks |
|---|---|---|
2 3 |
2 |
the sample |
1 5 |
0 |
one letter cannot contain two |
3 2 |
6 |
only two letters available |
1000000000000000000 26 |
994901695 |
upper bound |
The case 3 2 is worth enumerating by hand: of the eight
words over a and b, those containing both are
all except aaa and bbb, so 6.
Common mistakes.
- Using the formula
K^N − (K−2)^N. That is the answer for "containsaorb", a different problem. - Forgetting the
+ MODadjustment after the subtraction. - Computing
2 * tanpaSatuwithout% MOD, which for values close to the modulus is still safe inlong longbut breaks the standard form you should get used to.
Problem 3 — Pairs Summing to a Multiple of K
How to think about it. Whether x + y is
divisible by K is determined only by the remainder of
x and the remainder of y, not by their values.
So group the numbers 1 to N by their remainder modulo
K, then pair up the groups.
If x has remainder r, then y
must have remainder K − r — except for r = 0,
whose partner is also 0. The form (K − r) % K handles both
at once.
The number of numbers from 1 to N with remainder
r:
- for
r= 0:⌊N / K⌋ - for
r≥ 1:⌊(N − r) / K⌋ + 1, and 0 ifr > N
The answer is the sum of cacah[r] × cacah[(K − r) % K]
over all r. Since the pairs are ordered, there is no
division by two at the end.
Walkthrough.
vector<long long> cacah(k, 0);
cacah[0] = n / k;
for (long long r = 1; r < k; r++) {
if (r > n) break;
cacah[r] = (n - r) / k + 1;
}
long long hasil = 0;
for (long long r = 0; r < k; r++) {
hasil += cacah[r] * cacah[(k - r) % k];
}The cost is proportional to K, not N — 10⁶
steps for the largest constraints.
Size of the answer. The largest case is
K = 1, when every pair qualifies: N² = 10¹⁸.
It fits in long long, and this is why the constraint on
N in this problem is smaller than in the two previous
problems.
Tests.
| Input | Output | What it checks |
|---|---|---|
5 3 |
9 |
the sample |
1 1 |
1 |
one number |
7 10 |
5 |
K larger than N |
1000000000 1 |
1000000000000000000 |
upper bound |
The case 7 10 tests remainders that never appear: the
groups r = 8 and onward are empty, and the product must
yield zero, not read outside the array.
Common mistakes.
- Using
cacah[k - r]without% k, which forr= 0 readscacah[k]— out of bounds. - Dividing the result by 2. The pairs are ordered, so there is no division.
- Checking every pair
(x, y)with nested loops.
If you are still stuck
- Unsure whether to add or multiply → Chapter 16, the "or/and" table and the tree diagram.
- Something is counted twice → Chapter 16, section When the groups overlap.
- "At least one" problems feel hard → Chapter 16, the complement technique.
The most useful habit from this chapter: every counting formula is checked with direct enumeration on small data before it is used on large data.