E - 桁の積 / Product of Digits Editorial by admin
Claude 4.6 Opus (Thinking)Overview
Among integers \(n\) from \(L\) to \(R\) inclusive, this problem asks us to find the count of those where the product of digits \(f(n)\) equals \(K\), using digit DP.
Analysis
Problems with a Naive Approach
Since \(R\) can be up to \(10^{18}\), it is impossible to check every integer one by one (TLE).
Key Observations
Splitting the range: The answer for \([L, R]\) can be decomposed as “answer for \([1, R]\)” − “answer for \([1, L-1]\)”. This reduces the problem to finding the count of integers up to \(N\) that satisfy the condition.
Case \(K = 0\): \(f(n) = 0\) ⇔ some digit of \(n\) contains \(0\). This can be solved with a simple digit DP that counts “the number of integers containing \(0\)”.
Case \(K > 0\): Each digit must be one of \(1\)–\(9\) (since having a \(0\) would make the product \(0\)). Since the product of digits is a product of numbers from \(1\)–\(9\), the prime factors of \(K\) must only be \(2, 3, 5, 7\). If \(K\) has any other prime factor, the answer is immediately \(0\).
Small number of product states: Only divisors of \(K\) can appear as “intermediate products”. The number of divisors of \(K = 2^a \times 3^b \times 5^c \times 7^d\) (where \(K \leq 10^{18}\)) is at most a few hundred to a few thousand, which is manageable as states in digit DP.
Algorithm
Digit DP
For the upper bound \(N\) represented as a digit sequence \(d_0 d_1 \ldots d_{n-1}\) in decimal, we perform memoized recursion with the following states:
pos: the current digit position being consideredtight: whether the digits chosen so far exactly match the upper digits of \(N\) (upper bound constraint)started: whether we have passed the leading zero padding phase and the number has started- Case \(K = 0\):
has_zero— whether a digit \(0\) has already appeared - Case \(K > 0\):
prod_idx— the current product of digits, managed as an index in the list of divisors of \(K\)
Pruning for \(K > 0\)
When choosing a digit \(d\) at each position, we prune with the following conditions: - If \(d = 0\), the product becomes \(0\), so skip - If new product = (current product) \(\times d\) exceeds \(K\), skip - If the new product is not a divisor of \(K\), skip (impossible to reach \(K\))
Complexity
Let \(D\) be the number of digits of \(N\) (at most \(19\)), and \(M\) be the number of divisors of \(K\).
Case \(K = 0\):
- Time complexity: \(O(D \times 2 \times 2 \times 2 \times 10) = O(D)\)
- Space complexity: \(O(D)\)
Case \(K > 0\):
- Time complexity: \(O(D \times 2 \times 2 \times M \times 10)\)
- Space complexity: \(O(D \times M)\)
- When \(K \leq 10^{18}\) and the only prime factors are \(2, 3, 5, 7\), \(M\) is at most a few thousand (for example, \(2^{59}\) has about \(60\) divisors, and mixed cases don’t grow much larger)
Implementation Notes
count(R) - count(L-1)pattern: A standard digit DP technique that transforms a range problem into an upper-bound-only problem.startedflag: Necessary to correctly handle leading zeros (e.g., representations like “007”). Note that when \(K > 0\), zeros before the number starts are not considered as “zeros contained in the digits”.Indexing divisors: By managing states using indices in the list of divisors of \(K\) rather than the product values themselves, memoization is performed efficiently.
Prime factor check: When \(K > 0\) and \(K\) has prime factors other than \(2, 3, 5, 7\), no combination of digits can produce \(K\), so we can immediately output \(0\).
Source Code
import sys
from functools import lru_cache
def solve():
L, R, K = map(int, input().split())
# Count numbers in [1, N] with digit product == K
# Answer = count(R) - count(L-1)
# Special case: K = 0
# f(n) = 0 iff n contains digit 0
# Count of n in [1, N] with f(n)=0 = count of n in [1,N] with at least one 0 digit
# For K >= 1, digits are all 1-9
# The digit product is a product of digits from 1-9
# So K must factor into primes 2,3,5,7 only
# We use digit DP. For each number up to N, we track the running product.
# But the product can be huge. We need to be smart.
# Key insight: if K=0, we count numbers with at least one zero digit.
# If K>0, K must be expressible as product of digits 1-9, meaning
# K = 2^a * 3^b * 5^c * 7^d for some non-negative a,b,c,d.
# Also the product of digits 1-9 for an 18-digit number is at most 9^18 ~ 1.5 * 10^17 < 10^18.
# For K != 0: We do digit DP tracking the current product.
# The product at any point is a divisor of K (if we only consider paths that can reach K).
# We can prune: current product must divide K, and remaining digits must be able to
# multiply to reach K/current_product.
# Let's use memoization with (position, product_so_far, tight, started)
# product_so_far can be at most K, and must divide K.
# Number of divisors of K where K <= 10^18 can be large but since K = 2^a*3^b*5^c*7^d,
# the number of divisors is manageable (at most ~60*40*27*22 but K<=10^18 limits this).
# Actually for K that has prime factors other than 2,3,5,7, and K>0, answer is 0.
if K == 0:
# Count numbers in [L, R] that contain at least one '0' digit
def count_with_zero(N):
if N <= 0:
return 0
digits = list(map(int, str(N)))
n = len(digits)
@lru_cache(maxsize=None)
def dp(pos, tight, started, has_zero):
if pos == n:
if not started:
return 0
return 1 if has_zero else 0
limit = digits[pos] if tight else 9
res = 0
for d in range(0, limit + 1):
new_tight = tight and (d == limit)
new_started = started or (d != 0)
new_has_zero = has_zero or (started and d == 0)
res += dp(pos + 1, new_tight, new_started, new_has_zero)
return res
result = dp(0, True, False, False)
dp.cache_clear()
return result
print(count_with_zero(R) - count_with_zero(L - 1))
else:
# Check if K can be expressed as product of digits 1-9
# K must only have prime factors 2, 3, 5, 7
tmp = K
for p in [2, 3, 5, 7]:
while tmp % p == 0:
tmp //= p
if tmp != 1:
print(0)
return
# Get all divisors of K
def get_divisors(K):
divs = [1]
for p in [2, 3, 5, 7]:
new_divs = []
pk = 1
while pk <= K:
for d in divs:
if d * pk <= K and K % (d * pk) == 0:
new_divs.append(d * pk)
pk *= p
divs = list(set(new_divs))
return sorted(divs)
divisors = get_divisors(K)
div_index = {d: i for i, d in enumerate(divisors)}
nd = len(divisors)
def count_eq_K(N):
if N <= 0:
return 0
digits = list(map(int, str(N)))
n = len(digits)
@lru_cache(maxsize=None)
def dp(pos, tight, started, prod_idx):
if pos == n:
if not started:
return 0
return 1 if divisors[prod_idx] == K else 0
limit = digits[pos] if tight else 9
res = 0
for d in range(0, limit + 1):
new_tight = tight and (d == limit)
if not started and d == 0:
res += dp(pos + 1, new_tight, False, 0)
else:
if d == 0:
continue # product becomes 0, can't reach K>0
new_prod = divisors[prod_idx] * d if started else d
if new_prod > K or K % new_prod != 0:
continue
if new_prod not in div_index:
continue
res += dp(pos + 1, new_tight, True, div_index[new_prod])
return res
result = dp(0, True, False, 0)
dp.cache_clear()
return result
print(count_eq_K(R) - count_eq_K(L - 1))
solve()
This editorial was generated by claude4.6opus-thinking.
posted:
last update: