P-adic valuation
The $p$-adic valuation $v_p(n)$ is the exponent of the prime $p$ in $n$: the number of times I can divide by $p$ before a remainder appears. It turns a multiplicative question into an additive one. "Does $10^{12}$ divide this coefficient?" becomes "are two integers at least 12?" The problems below all involve numbers far too large to build: factorials, multinomials, products of whole rows of Pascal's triangle. In each of them I stop holding the number and hold its valuations instead.
The idea
Every positive integer is a vector of exponents, $n = \prod_p p^{v_p(n)}$, with $v_p(0) = \infty$ by convention. Four rules make that vector easy to work with:
| Rule | Statement | What it buys |
|---|---|---|
| Product | $v_p(ab) = v_p(a) + v_p(b)$ | Products, powers and quotients become sums and differences of exponents |
| Divisibility | $a \mid b \iff v_p(a) \le v_p(b)$ for every $p$ | Divisibility is a component-wise comparison; $\gcd$ is the min, $\operatorname{lcm}$ the max |
| Sum (ultrametric) | $v_p(a+b) \ge \min(v_p(a), v_p(b))$, with equality when the two differ | The valuation of a sum is known exactly whenever one term is strictly "less divisible" |
| Factorials | $v_p(n!) = \sum_{i\ge1} \lfloor n/p^i \rfloor = \dfrac{n - s_p(n)}{p-1}$ | One huge product, $\log_p n$ divisions (Legendre) |
The last rule carries over to binomial and multinomial coefficients, which are quotients of factorials. Kummer's theorem gives the result as the number of carries when the parts are added in base $p$. In digit-sum form, $v_p\binom{n}{i,j,k} = \big(s_p(i)+s_p(j)+s_p(k)-s_p(n)\big)/(p-1)$.
The sum rule is the one engineers tend to forget. The other three treat $v_p$ like a logarithm. The sum rule is what lets you reason about $v_p$ of an expression that is not a product.
How the solutions use it
Divisibility as exponent comparison: 154 and 549. Problem 154 counts the coefficients of
$(x+y+z)^{200000}$ that are divisible by $10^{12}$. That means $v_2 \ge 12$ and $v_5 \ge 12$, and
Kummer turns each condition into a digit-sum inequality over a precomputed table. Neither language
ever builds a coefficient. The two sides differ only in how they scan the $O(n^2)$ triples. The
Python version vectorises each row with numpy: it adds s[:m+1] to the reversed s[i:], so every
$j + k = m$ pair is lined up at once. The C version walks $j$ only up to $m/2$ and counts each
unordered pair twice. It also builds its digit sums with the one-line recurrence
out[x] = out[x / base] + x % base into unsigned char arrays, which keeps both tables in cache.
Problem 549 asks for the least $m$ with $n \mid m!$. The divisibility rule splits this prime by prime:
$s(n) = \max_{p^e \parallel n} s(p^e)$, where $s(p^e)$ is the least $m$ with $v_p(m!) \ge e$. A
linear sieve supplies each $i$'s smallest prime and its exponent,
and that is all the max needs. The two versions run the same loop. Python loops over numpy arrays
element by element. C grows a per-prime cache of $s(p^e)$ with realloc.
Carrying the exponent vector instead of the number: 650. $B(n) = \prod_k \binom{n}{k}$ has
millions of digits. Its divisor sum $\sigma$, however, is
multiplicative, so it needs only
$e_p = v_p(B(n)) = (n+1)\,v_p(n!) - 2\sum_{j\le n} v_p(j!)$. The product rule makes the step from
$n-1$ to $n$ a small change in exponent. For every prime that does not divide $n$, that change is
the same expression, $-v_p((n-1)!)$. So one blanket update covers almost every prime, and only the few
primes dividing $n$ need a fix-up. The language split is about keeping modular products in 64 bits.
The Python version keeps $p^{e_p} \bmod m$ for all primes in a numpy int64 vector and reduces the
final product pairwise, so no intermediate exceeds $m^2 < 2^{63}$. The C version has no vector to
protect and multiplies through __int128.
Splitting a number into $p$-part and unit: 160. The last five non-zero digits of $N!$ are
$N!/10^{v_5(N!)} \bmod 10^5$. Writing $N! = 2^{v_2} 5^{v_5} u$ separates what is not invertible mod
$2^5$ and $5^5$ (the prime powers) from what is (the unit $u$). Each half is solved on its own and
then recombined by the CRT. The valuations are what
keep the leftover powers of 2 and 5 exact; the residues alone would lose them. Python gets the inverse
from pow(x, -z, m). C hand-rolls an extended-Euclid modinv.
The sum rule doing real work: 484 and 834. Problem 484 needs $\gcd(k, k')$, where $k'$ is the
arithmetic derivative. Write $k'/k = \sum_q a_q/q$ over
$q^{a_q} \parallel k$. Here the ultrametric rule decides everything. If $p \nmid a_p$, the term $a_p/p$
has valuation $-1$ and every other term has valuation $\ge 0$. The minimum is strict, so
$v_p(k') = a_p - 1$ exactly. If $p \mid a_p$, every term has valuation $\ge 0$ and $v_p(k') \ge a_p$.
Either way the gcd is a clean per-prime formula, which makes it multiplicative and lets a
squarefull-number DFS sum it (Dirichlet convolution).
Problem 834 reduces to deciding which $q > n$ divide $q(q-1)/2 + X$, where $X = n(n-1)/2$. Odd $q$ must
divide $X$. For even $q$, the first term is only $\equiv q/2 \pmod q$, so the condition becomes
$X \equiv q/2$. That pins the 2-adic valuation: $v_2(q) = v_2(X) + 1 = v_2(n) + v_2(n-1)$, exactly.
Every valid $q$ is therefore an odd divisor $d$ or $d \cdot 2^{v_2(n)+v_2(n-1)}$, never anything in
between. Both languages strip the 2s with a shift loop (while (!(x & 1)) { v++; x >>= 1; }) before
the SPF factorisation. A __builtin_ctzll would do the same in one instruction, but the loop is not
the bottleneck.
How to reason about it
- Reach for it whenever the question says "divisible by", "trailing zeros", "last non-zero digits" or "number of divisors" about a product. Those depend only on the exponent vector. Computing that vector costs logarithms, where computing the number costs its digits.
- Composite targets split by prime. $10^{12}$ is two separate conditions, $v_2 \ge 12$ and $v_5 \ge 12$. Remember that the scarcer prime (5, for factorials) is the one that decides.
- Track deltas, not totals. When a product changes by one factor per step (650, Pascal rows, running factorials), the valuations change by small integers. Update those integers; do not re-factor.
- Use the sum rule deliberately. Equality in $v_p(a+b) = \min$ when the valuations differ is what turns "some sum is divisible" into an exact exponent (484, 834). When the valuations tie, the bound is only a bound, and that case needs a separate look.
- Pitfalls. $v_p(0)$ is infinite, so guard zero terms. Valuations of factorials grow like
$n/(p-1)$, so they fit an
intlong after $p^{v}$ has overflowed everything. Keep the exponent and reduce the power modulo something only at the end. For $p = 2$ in C, the valuation is the trailing-zero count of the binary word (power of two).
Problems10 / 13
- ● 0154 — Exploring Pascal's Pyramid
- ● 0160 — Factorial Trailing Digits
- ○ 0383 — Divisibility Comparison Between Factorials
- ● 0484 — Arithmetic Derivative
- ● 0549 — Divisibility of Factorials
- ● 0561 — Divisor Pairs
- ● 0650 — Divisors of Binomial Product
- ○ 0712 — Exponent Difference
- ● 0779 — Prime Factor and Exponent
- ○ 0792 — Too Many Twos
- ● 0834 — Add and Divide
- ● 0850 — Fractions of Powers
- ● 1003 — Lonely Singles