Pigeonhole principle. If objects go into boxes, some box gets at least . The art is choosing the boxes.
| Puzzle | Boxes | Answer |
|---|---|---|
| socks of colours in the dark: how many to be sure of a pair? | colours | |
| … to be sure of socks of one colour | colours | |
| 10 black and 10 white socks, sure of a black pair | worst case: all whites first | |
| gloves: 10 pairs, sure of a matching left and right | worst case: all lefts | |
| 367 people: two share a birthday | 366 days | always |
| among people, two have the same number of friends at the party | degrees , but and cannot both occur | always |
| handshake lemma: the number of people who shook an odd number of hands is even | always | |
| integers: a nonempty contiguous block with sum divisible by | prefix sums mod | always, |
| numbers from : two are coprime | pairs | two consecutive numbers |
| numbers from : one divides another | odd parts | same odd part, powers of 2 differ |
| 5 lattice points: the midpoint of two is a lattice point | parities | always |
| 5 points in a unit square: two within | four squares | always |
| any distinct numbers: increasing run of or decreasing of (Erdős–Szekeres) | labels (longest increasing ending here, longest decreasing ending here) | always, tight |
| Dirichlet: for real and , some has | fractional parts of in intervals | always |
| two Londoners with the same number of hairs | hair counts population | always |
| party of 6: three mutual friends or strangers | see Party Problem | Ramsey |
pair<int, int> divisibleBlock(const vector<ll>& a) { // [l, r) with sum divisible by n = |a|
int n = sz(a); vector<int> seen(n, -1); seen[0] = 0; ll s = 0;
for (int i = 0; i < n; i++) {
s = ((s + a[i]) % n + n) % n;
if (seen[s] >= 0) return {seen[s], i + 1};
seen[s] = i + 1;
}
return {-1, -1}; // unreachable for n >= 1
}
pair<int, int> dividingPair(const vector<int>& v) { // distinct values in 1..2n, |v| = n + 1: indices i, j with v[i] | v[j]
int n = sz(v) - 1; vector<int> byOdd(2 * n + 1, -1);
for (int i = 0; i <= n; i++) {
int o = v[i]; while (o % 2 == 0) o /= 2;
if (byOdd[o] >= 0) { int j = byOdd[o]; return v[j] < v[i] ? pair{j, i} : pair{i, j}; }
byOdd[o] = i;
}
return {-1, -1};
}
vector<int> erdosSzekeres(const vector<int>& a, int r, int s) { // |a| >= r s + 1 distinct values
int n = sz(a); vector<int> inc(n, 1), dec(n, 1), pi(n, -1), pd(n, -1);
for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) {
if (a[j] < a[i] && inc[j] + 1 > inc[i]) inc[i] = inc[j] + 1, pi[i] = j;
if (a[j] > a[i] && dec[j] + 1 > dec[i]) dec[i] = dec[j] + 1, pd[i] = j;
}
for (int i = 0; i < n; i++) {
auto build = [&](const vector<int>& par) { vector<int> idx; for (int k = i; k >= 0; k = par[k]) idx.push_back(k); reverse(all(idx)); return idx; };
if (inc[i] > r) { auto idx = build(pi); idx.resize(r + 1); return idx; }
if (dec[i] > s) { auto idx = build(pd); idx.resize(s + 1); return idx; }
}
return {}; // only if |a| <= r s
}
pair<int, int> latticeMidpoint(const vector<pair<ll, ll>>& p) { // >= 5 points: midpoint of p[i], p[j] is integral
int first[4] = {-1, -1, -1, -1};
for (int i = 0; i < sz(p); i++) {
int c = int((p[i].first & 1) * 2 + (p[i].second & 1));
if (first[c] >= 0) return {first[c], i};
first[c] = i;
}
return {-1, -1};
}Proof: the counting puzzles
Pigeonhole. If every box had at most objects, there would be fewer than . Socks: socks can all differ, cannot; for of a colour, socks can have of each colour, one more forces . Black pair / gloves: the adversary hands out all whites (all lefts) first; after that, every further pick helps, and 2 blacks (1 right) suffice.
Same number of friends. Degrees lie in . Someone with friends knows everyone, so nobody has 0; hence the degrees take at most values, and two coincide. Handshake lemma: each handshake adds 2 to , so the sum is even, and so the number of odd terms is even.
Divisible block. The prefix sums take at most residues mod , so for some , and .
. Split into the pairs : two chosen numbers share a pair, and consecutive numbers are coprime. Write each number as with odd; there are only odd numbers below , so two chosen numbers share , and the one with the smaller power of 2 divides the other. Both bounds are tight: has no coprime pair and no dividing pair.
Lattice midpoints and the unit square. The midpoint of is integral iff and ; 5 points in 4 parity classes. Splitting the unit square into four squares of side puts two of 5 points in one small square, whose diameter is .
Erdős–Szekeres. Label position with , the longest increasing and decreasing subsequences ending at . For : if then , otherwise ; so labels are distinct. If all and all , there are at most labels for positions. Tight: increasing blocks of length , the blocks in decreasing order. Dirichlet: the numbers , , fall into the intervals , so two, and with , differ by less than ; then works. All four functions were checked against brute force on random inputs.
Related notes
- Invariants, Parity and Extremal Arguments
- Permutation Constructions (Erdős–Szekeres constructions)
- Party Problem
- Birthday Problem (the probabilistic version)