E - スムーズな山道 / Smooth Mountain Path 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This problem asks us to count positive integers whose “step sequence” (formed by the absolute differences of adjacent digits) is monotonically non-decreasing. We use digit DP to find how many such numbers exist between \(L\) and \(R\) inclusive.
Analysis
Key Observations
- Since \(R\) can be as large as \(10^{5000}\), it is impossible to check each number one by one.
- “The count of numbers satisfying the condition between \(L\) and \(R\) inclusive” can be computed as \(f(R) - f(L-1)\), where \(f(N) = \) “the count of numbers satisfying the condition between \(1\) and \(N\) inclusive.”
- \(f(N)\) can be efficiently computed using Digit DP.
Why Digit DP is Effective
The information needed to determine whether a number is a smooth mountain path is: - The previous digit (to compute the next difference) - The previous step \(e_{i-1}\) (to verify the monotonically non-decreasing condition \(e_{i-1} \leq e_i\))
Both of these fall within the range \(0\)–\(9\), so the number of states is very small and can be managed as digit DP states.
Algorithm
Digit DP States
We determine each digit from the most significant to the least significant, managing the following states:
| State Element | Range of Values | Description |
|---|---|---|
last_d |
\(0\)–\(9\) or “none” | The digit placed at the previous position |
last_e |
\(0\)–\(9\) or “none” | The previous step (occurs from the 2nd digit onward) |
tight |
\(0\) or \(1\) | Whether we are still bounded by the upper limit \(N\) |
started |
\(0\) or \(1\) | Whether a non-zero digit has been placed (leading zero handling) |
Transitions
When placing digit \(d\) at position pos:
- The number has not started yet (\(started = 0\)) and \(d = 0\): Ignore as a leading zero and proceed to the next position without changing state.
- First non-zero digit: Set \(last\_d = d\), and the step is still “none.”
- Second digit: Compute the new step \(e_{new} = |last\_d - d|\) and set \(last\_e = e_{new}\).
- Third digit onward: Compute \(e_{new} = |last\_d - d|\), and transition is only possible when \(e_{new} \geq last\_e\).
Range Handling
\[\text{Answer} = f(R) - f(L-1) \pmod{10^9 + 7}\]
\(L - 1\) is computed by performing borrow (subtraction with carry) processing on the string representation.
Concrete Example
Consider the digit DP for \(N = 1234\). In the state \(tight = 1\), each digit is restricted to be at most the corresponding digit of \(N\). For example, if we choose \(d = 1\) at the first position and remain with \(tight = 1\), then the second digit is restricted to \(0\)–\(2\). Once \(tight = 0\), we are free to choose any digit from \(0\)–\(9\) for subsequent positions.
Complexity
Estimating the number of states:
- last_d: \(11\) possibilities (\(0\)–\(9\) + none)
- last_e: \(11\) possibilities (\(0\)–\(9\) + none)
- tight: \(2\) possibilities
- started: \(2\) possibilities
The maximum number of states at each position is \(11 \times 11 \times 2 \times 2 = 484\), with at most \(10\) transitions from each state.
- Time complexity: \(O(N \times 484 \times 10) = O(N)\) (\(N\) is the number of digits, at most \(5000\))
- Space complexity: \(O(484) = O(1)\) (using dictionaries at each position, keeping only the states from the previous position)
Implementation Notes
Leading zero handling: For example, when performing DP with a 3-digit upper bound, the
startedflag is necessary to correctly count 1-digit and 2-digit numbers as well.Computing \(L - 1\): Since large integers are handled as strings, borrows must be processed manually. When \(L = 1\), \(L - 1 = 0\), which is handled as \(f(0) = 0\).
Dictionary-based DP: Since only reachable states are stored, not all 484 states actually appear in practice, making it efficient.
MOD arithmetic: Since the count can become enormous due to the large number of digits, we take the remainder modulo \(10^9 + 7\) at each transition.
Source Code
import sys
from functools import lru_cache
def solve():
MOD = 10**9 + 7
input_data = sys.stdin.read().split()
L = input_data[0]
R = input_data[1]
# Count smooth numbers in [1, N] for a given string N
# We use digit DP
# State: position, last_digit, last_diff (the previous e value), tight, started
# last_diff ranges from 0 to 9, we use -1 to mean "no previous diff yet"
def count_smooth(N_str):
"""Count smooth numbers in [1, N_str]"""
n = len(N_str)
digits = [int(c) for c in N_str]
# dp with memoization
# pos: current position (0..n-1)
# last_d: last digit placed (0-9), -1 if not started
# last_e: last diff value (0-9), -1 if no diff yet (0 or 1 digits placed)
# tight: whether we're still bounded by N
# started: whether we've placed a nonzero digit yet
# We'll use iterative DP with dictionary or array
# States: (pos, last_d, last_e, tight, started)
# last_d: 0-9 or -1 (use 10 for -1)
# last_e: 0-9 or -1 (use 10 for -1)
# tight: 0 or 1
# started: 0 or 1
# Use dictionary-based DP for efficiency
# Current states -> count
# state = (last_d, last_e, tight, started)
# We process position by position
current = {}
# Initial state before placing any digit
# last_d=10(none), last_e=10(none), tight=1, started=0
current[(10, 10, 1, 0)] = 1
for pos in range(n):
nxt = {}
for (ld, le, tight, started), cnt in current.items():
if cnt == 0:
continue
limit = digits[pos] if tight else 9
for d in range(0, limit + 1):
new_tight = 1 if (tight and d == limit) else 0
if not started and d == 0:
# Still haven't started
key = (10, 10, new_tight, 0)
nxt[key] = (nxt.get(key, 0) + cnt) % MOD
else:
# Placing digit d
if not started:
# First digit
# d >= 1
key = (d, 10, new_tight, 1)
nxt[key] = (nxt.get(key, 0) + cnt) % MOD
else:
# We have a previous digit ld
new_e = abs(ld - d)
if le == 10:
# No previous diff, this is the second digit
key = (d, new_e, new_tight, 1)
nxt[key] = (nxt.get(key, 0) + cnt) % MOD
else:
# Check monotone non-decreasing: new_e >= le
if new_e >= le:
key = (d, new_e, new_tight, 1)
nxt[key] = (nxt.get(key, 0) + cnt) % MOD
# else: skip, not smooth
current = nxt
# Sum all states that have started
result = 0
for (ld, le, tight, started), cnt in current.items():
if started:
result = (result + cnt) % MOD
return result
def subtract_one(s):
"""Return string representation of int(s) - 1, assuming s >= 1"""
digits = list(s)
i = len(digits) - 1
while i >= 0 and digits[i] == '0':
digits[i] = '9'
i -= 1
# digits[i] is nonzero
digits[i] = str(int(digits[i]) - 1)
# Remove leading zeros
result = ''.join(digits).lstrip('0')
if result == '':
result = '0'
return result
# Answer = count_smooth(R) - count_smooth(L-1)
L_minus_1 = subtract_one(L)
ans_R = count_smooth(R)
if L_minus_1 == '0':
ans_L = 0
else:
ans_L = count_smooth(L_minus_1)
print((ans_R - ans_L) % MOD)
solve()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: