Binomial Coefficients Divisible by 10
Let T(m, n) denote the number of binomial coefficients C(i, n) that are divisible by 10, for n <= i < m (where i, m, n are positive integers). Given: T(10^9, 10^7 - 10) = 989697000. Find: T(10^18,...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Let \(T(m, n)\) be the number of the binomial coefficients \(^iC_n\) that are divisible by \(10\) for \(n \le i < m\) (\(i\), \(m\) and \(n\) are positive integers).
You are given that \(T(10^9, 10^7-10) = 989697000\).
Find \(T(10^{18}, 10^{12}-10)\).
Problem 322: Binomial Coefficients Divisible by 10
Solution
Inclusion—exclusion via Lucas’ theorem
Theorem 1 (Lucas). Let be a prime. Write and in base . Then
Corollary 1. if and only if for every digit position (digit-wise domination).
Proof. Each factor is nonzero in if and only if . The product vanishes if and only if at least one factor vanishes.
Definition 1. For a prime , let
For the composite modulus , let
Theorem 2 (Inclusion—exclusion). Since with ,
Proof. For each , define events and . Then iff holds. By inclusion—exclusion,
where , , and . Equivalently, .
Single-prime digit DP
Proposition 1. For a prime , equals the number of integers whose base- digits dominate those of position-by-position, minus 1 if itself satisfies the domination condition. This count is evaluated by a standard digit DP on the base- representation of , processing digits from the most significant to the least significant, with a single boolean state recording whether the prefix constructed so far equals that of (tight) or is strictly less (free).
Proof. By Corollary 1, iff every base- digit of is at least the corresponding digit of . The digit DP correctly enumerates all such in by enforcing digit-wise constraints: at each position , the chosen digit satisfies where if the prefix is tight, and otherwise.
Joint digit DP for
Theorem 3. When , the count is computed by a DP in base 10. At digit position (from the least significant), the state is a pair of carry values encoding the partially computed base-2 and base-5 representations of . For a base-10 digit at position :
The digit is accepted only if both extracted base- digits dominate the corresponding digits of . After all positions, the final carries must also satisfy domination for any remaining high-order digits of .
Proof. Since , each base-10 digit at position contributes to the base-2 representation (after factoring out powers of 2) and to the base-5 representation (after factoring out powers of 5). The carry variables and track the values and needed to reconstruct higher-order digits. Since is always odd, gives the correct base-2 digit at position . The domination constraint from Corollary 1 is enforced digit by digit.
Editorial
T(m, n) = #{i in [n, m) : 10 | C(i, n)}. By inclusion-exclusion on primes 2 and 5: T = (m - n) - f_2 - f_5 + f_10 where f_p counts i with p not dividing C(i,n) (digit-wise domination in base p). f_10 uses a joint base-10 digit DP tracking carry states for base-2 and base-5.
Pseudocode
f2 = digit_dp_single_prime(m, n, 2)
f5 = digit_dp_single_prime(m, n, 5)
f10 = joint_digit_dp_base10(K = log10(m), n)
Return (m - n) - f2 - f5 + f10
Complexity
- Time: for , for (where is the number of base- digits of ), and for where is the number of reachable carry states. Dominant term: .
- Space: per DP layer.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
vector<int> to_base(ll x, int p) {
if (x == 0) return {0};
vector<int> d;
while (x > 0) { d.push_back(x % p); x /= p; }
return d;
}
ll count_geq_digits(ll m, ll n, int p) {
auto nd = to_base(n, p), md = to_base(m, p);
int L = max(nd.size(), md.size());
nd.resize(L, 0);
md.resize(L, 0);
reverse(nd.begin(), nd.end());
reverse(md.begin(), md.end());
vector<ll> dp(2, 0);
dp[1] = 1;
for (int pos = 0; pos < L; pos++) {
vector<ll> ndp(2, 0);
for (int t = 0; t < 2; t++) {
if (!dp[t]) continue;
int upper = t ? md[pos] : (p - 1);
for (int d = nd[pos]; d <= upper; d++) {
int nt = t && (d == md[pos]) ? 1 : 0;
ndp[nt] += dp[t];
}
}
dp = ndp;
}
ll res = dp[0] + dp[1];
bool all_ok = true;
for (int j = 0; j < L; j++)
if (md[j] < nd[j]) { all_ok = false; break; }
if (all_ok) res--;
return res;
}
ll count_f10(ll m, ll n) {
auto n2 = to_base(n, 2), n5 = to_base(n, 5);
int K = (int)round(log10((double)m));
ll n_high2 = n >> K;
ll p5k = 1;
for (int i = 0; i < K; i++) p5k *= 5;
ll n_high5 = n / p5k;
map<pll, ll> states;
states[{0, 0}] = 1;
ll pow5 = 1, pow2 = 1;
for (int k = 0; k < K; k++) {
map<pll, ll> ns;
int n2k = (k < (int)n2.size()) ? n2[k] : 0;
int n5k = (k < (int)n5.size()) ? n5[k] : 0;
for (auto &[st, cnt] : states) {
ll c2 = st.first, c5 = st.second;
for (int d = 0; d < 10; d++) {
if ((int)((c2 + d) % 2) < n2k) continue;
if ((int)((c5 + d * pow2) % 5) < n5k) continue;
ll nc2 = (c2 + d * pow5) / 2;
ll nc5 = (c5 + d * pow2) / 5;
ns[{nc2, nc5}] += cnt;
}
}
states = ns;
pow5 *= 5;
pow2 *= 2;
}
ll total = 0;
for (auto &[st, cnt] : states) {
ll c2 = st.first, c5 = st.second;
if (n_high2 > 0 && (c2 & n_high2) != n_high2) continue;
if (n_high5 > 0) {
auto nh = to_base(n_high5, 5), ch = to_base(c5, 5);
bool ok = true;
for (int j = 0; j < (int)nh.size(); j++) {
int cj = (j < (int)ch.size()) ? ch[j] : 0;
if (cj < nh[j]) { ok = false; break; }
}
if (!ok) continue;
}
total += cnt;
}
return total;
}
int main() {
ll m = 1000000000000000000LL;
ll n = 999999999990LL;
ll f2 = count_geq_digits(m, n, 2);
ll f5 = count_geq_digits(m, n, 5);
ll f10 = count_f10(m, n);
cout << (m - n) - f2 - f5 + f10 << endl;
return 0;
}
"""
Problem 322: Binomial Coefficients Divisible by 10
T(m, n) = #{i in [n, m) : 10 | C(i, n)}.
By inclusion-exclusion on primes 2 and 5:
T = (m - n) - f_2 - f_5 + f_10
where f_p counts i with p not dividing C(i,n) (digit-wise domination in base p).
f_10 uses a joint base-10 digit DP tracking carry states for base-2 and base-5.
"""
import math
from functools import lru_cache
def to_base(x, p):
if x == 0:
return [0]
d = []
while x > 0:
d.append(x % p)
x //= p
return d
def count_geq_digits(m, n, p):
"""Count i in [0, m) with every base-p digit of i >= that of n."""
nd = to_base(n, p)
md = to_base(m, p)
L = max(len(nd), len(md))
nd += [0] * (L - len(nd))
md += [0] * (L - len(md))
nd_r = nd[::-1]
md_r = md[::-1]
@lru_cache(maxsize=None)
def dp(pos, tight):
if pos == L:
return 1
upper = md_r[pos] if tight else (p - 1)
lower = nd_r[pos]
if lower > upper:
return 0
total = 0
for d in range(lower, upper + 1):
total += dp(pos + 1, tight and (d == md_r[pos]))
return total
result = dp(0, True)
# Exclude i = m (we want [0, m), and dp counts [0, m])
if all(md_r[j] >= nd_r[j] for j in range(L)):
result -= 1
return result
def count_f10_dp(m, n):
"""Count i in [0, m) with gcd(C(i,n), 10) = 1, via base-10 digit DP."""
n2 = to_base(n, 2)
n5 = to_base(n, 5)
K = round(math.log10(m))
assert 10**K == m
n_high2 = n >> K
n_high5 = n // (5**K)
def check_end(c2, c5):
if n_high2 > 0 and (c2 & n_high2) != n_high2:
return False
if n_high5 > 0:
nh5d = to_base(n_high5, 5)
c5d = to_base(c5, 5) if c5 > 0 else [0]
for j in range(len(nh5d)):
cj = c5d[j] if j < len(c5d) else 0
if cj < nh5d[j]:
return False
return True
states = {(0, 0): 1}
pow5, pow2 = 1, 1
for k in range(K):
new_states = {}
n2k = n2[k] if k < len(n2) else 0
n5k = n5[k] if k < len(n5) else 0
for (c2, c5), cnt in states.items():
for d in range(10):
b2 = (c2 + d) % 2
if b2 < n2k:
continue
b5 = (c5 + d * pow2) % 5
if b5 < n5k:
continue
nc2 = (c2 + d * pow5) // 2
nc5 = (c5 + d * pow2) // 5
key = (nc2, nc5)
new_states[key] = new_states.get(key, 0) + cnt
states = new_states
pow5 *= 5
pow2 *= 2
total = 0
for (c2, c5), cnt in states.items():
if check_end(c2, c5):
total += cnt
return total
def solve():
m = 10**18
n = 10**12 - 10
f2 = count_geq_digits(m, n, 2)
f5 = count_geq_digits(m, n, 5)
f10 = count_f10_dp(m, n)
print((m - n) - f2 - f5 + f10)
if __name__ == "__main__":
solve()