How many x give x! exactly k trailing zeroes? You never touch a factorial. You count factors of five, notice the count only climbs, and binary-search a number line that has no array.
Let f(x) = the number of trailing zeroes in x!. Given k (up to 10⁹),
count the non-negative integers x with f(x) = k — the preimage of
k. For k=0: 0!,1!,2!,3!,4! all end in no zero, so the answer is 5. For
k=5: the answer is 0 — and understanding why is the whole problem.
A trailing zero is a factor of 10 = 2 × 5. Inside 1 × 2 × … × x there are always more 2s than 5s,
so fives are the bottleneck. Count them and you have the zero count — no giant number required:
f(x) = ⌊x/5⌋ + ⌊x/25⌋ + ⌊x/125⌋ + …
The extra terms matter because 25 = 5² hides two fives, 125 = 5³ hides three. So
f(25) = 5 + 1 = 6, jumping straight past 5. That single jump is why k=5 has no
preimage.
Each step is exactly five wide, so every reachable height has five x's on it. But at powers of five the stair rises two levels at once — that skipped level has zero x's.
There's no list to index into. The candidates are the numbers 0…5k themselves, and you probe them by asking a yes/no question — binary search on the answer, not on memory.
Find where level k begins and where the next level begins. The gap between those two edges is the block width — 5 if the level exists, 0 if it was skipped.
Plot f(x) for small x. Every level is five wide — until x = 25, where
the count leaps from 4 to 6. Level 5 is a floor that no step ever lands on.
So the preimage size is always 5 or 0 — never 1, 2, 3, 4. If a level exists, five consecutive x's sit on it; if a jump skipped it, none do.
The x's with f(x) = k form one contiguous block, so count it by its edges:
lowerBound(k) (first x with f(x) ≥ k) and lowerBound(k+1). Each edge is a
binary search over the number line [0, 5(k+1)] — no array, just the monotonic f. Watch
one lower-bound search below: f(mid) ≥ target keeps the
left half (answer could be mid); f(mid) < target
discards it.
Try k=5 (both edges land on 25 → answer 0) or
k=0 (edges 0 and 5 → answer 5). Switch the edge dropdown to watch each half of the subtraction.
| k | lowerBound(k) | lowerBound(k+1) | difference | the x's |
|---|---|---|---|---|
| 0 | 0 (f(0)=0) | 5 (f(5)=1) | 5 | 0,1,2,3,4 |
| 5 | 25 (f(25)=6) | 25 (f(25)=6) | 0 | none |
| 6 | 25 (f(25)=6) | 30 (f(30)=7) | 5 | 25…29 |
For k=5, both edges snap to 25 because f never equals 5 — the subtraction returns 0
with no special-casing. The formula silently absorbs the skipped-level fact.
int preimageSizeFZF(int k) { return (int)(lowerBound(k + 1L) - lowerBound(k)); } // first x with f(x) >= target long lowerBound(long target) { long lo = 0, hi = 5L * (target + 1); while (lo < hi) { long mid = lo + (hi - lo) / 2; if (trailingZeroes(mid) >= target) hi = mid; else lo = mid + 1; } return lo; } long trailingZeroes(long x) { long c = 0; while (x > 0) { x /= 5; c += x; } // x/5 + x/25 + x/125 + … return c; }
O(log² k) time, O(1) space. The outer search is O(log k) steps; each computes
f(mid) in O(log₅ mid). Both edges together are two such searches.
lowerBound(target) searches for the smallest x with f(x) ≥ target. Because
(x+1)! = x! · (x+1) can only add factors of ten, f never decreases — so the predicate
reads false…false true…true and flips exactly once. When f(mid) ≥ target, mid might be
that first true, so keep it (hi = mid); otherwise discard it (lo = mid+1).
The preimage is one contiguous block precisely because f is monotonic. Its size is
lowerBound(k+1) − lowerBound(k): where level k+1 starts, minus where level
k starts. A skipped level makes both edges coincide, yielding 0 with no special case.
x! overflows even long almost immediately. The problem exists to make you count
factors instead.
Misses the extra fives in 25, 125, 625… You need the full x/5 + x/25 + … sum.
0! = 1, so f(0)=0. That's why k=0 has five x's (0–4), not four.
Jumps at powers of five skip levels. f(24)=4, f(25)=6 — level 5 never appears.
Equality isn't monotonic (false…true…false), so binary search breaks. Use
f(mid) ≥ target.
5·(k+1) reaches ~5×10⁹ > INT_MAX. Everything in the search must be long.
There's no array here — the candidates are numbers in [0, 5k], probed by a monotonic predicate.
That's binary search on the answer: choose a candidate, evaluate a monotone yes/no condition,
discard half the numeric range. It's the clearest proof that binary search is a strategy for monotonic search
spaces, not just sorted memory.
Two ideas stacked: replace an impossible computation with a factor count, then binary-search the monotone function's domain instead of enumerating inputs. That combination is what makes this more than a factorial puzzle.