E - スムーズな山道 / Smooth Mountain Path 解説 by admin
gemini-3.5-flash-thinkingOverview
This problem asks us to count the integers within a given interval \([L, R]\) such that the differences between adjacent digits (steps) are monotonically non-decreasing (equal to or greater than the previous) when reading from left to right. Since the number of digits can be up to \(5000\), which is extremely large, it is impossible to check each integer one by one. By using Digit DP, a standard technique for counting problems involving huge numbers, we can solve this efficiently.
Analysis
1. Reformulating the Range
Since it is difficult to directly count the integers satisfying the condition in the interval \([L, R]\), we use the following property: $\((\text{count in } [L, R]) = (\text{count from 1 to } R) - (\text{count from 1 to } L) + (\text{whether } L \text{ itself satisfies the condition (0 or 1)})\)\( This allows us to focus on designing a function `solve(X)` that computes "the number of smooth mountain paths from \)1\( to \)X\(" for a given integer \)X$.
2. Designing the Digit DP
We consider a digit DP that determines digits sequentially from the most significant digit (leftmost). To determine whether a number is a “smooth mountain path,” the following information is needed: - What digit was placed in the previous position (to compute the difference with the current digit) - What was the previous step size (to check whether the current step is “at least as large as the previous step”)
Additionally, the following standard digit DP states are needed:
- Whether the value being constructed is confirmed to be strictly less than the upper bound \(X\) (less)
- Whether a digit of \(1\) or greater has already appeared (is_start). This is essential for ignoring leading zeros.
Combining these, the DP state can be defined as follows:
dp[less][is_start][prev_digit][prev_diff]
- less: Whether it is confirmed to be less than the upper bound (\(0\): not confirmed, \(1\): confirmed)
- is_start: Whether the number has started (\(0\): not yet (all 0s), \(1\): started)
- prev_digit: The digit in the previous position (\(0 \le \text{prev\_digit} \le 9\))
- prev_diff: The previous step size (\(0 \le \text{prev\_diff} \le 9\). When no step exists yet, use \(10\) as a sentinel value)
Algorithm
DP Transitions
Let \(d\) (\(0 \le d \le 9\)) be the digit placed at the current position.
When the number has not started yet (
is_start == 0)- When \(d = 0\):
The number remains unstarted. The state is not updated, and the previous step remains in the non-existent state (
10). - When \(d > 0\):
The number starts here.
next_is_start = 1, the previous digit becomes \(d\), and the previous step is10since no step exists yet.
- When \(d = 0\):
The number remains unstarted. The state is not updated, and the previous step remains in the non-existent state (
When the number has already started (
is_start == 1)- Let \(e = |d - \text{prev\_digit}|\) be the difference (current step) between the previous digit
prev_digitand the current digit \(d\). - If
prev_diff == 10(this is the first step), the transition is unconditionally valid. The next previous step becomes \(e\). - If
prev_diff != 10, the transition is valid only if the monotonically non-decreasing condition \(e \ge \text{prev\_diff}\) is satisfied. The next previous step becomes \(e\).
- Let \(e = |d - \text{prev\_digit}|\) be the difference (current step) between the previous digit
Initial State
dp[0][0][0][10] = 1- All other states are
0
Aggregating the Answer
After performing transitions for all digits, the sum of all states where is_start == 1 (representing actually existing positive integers) gives the desired count.
Complexity
Time Complexity
- Digit DP transitions: Let \(N\) (up to \(5000\)) be the number of digits. The number of DP states is \(2 \times 2 \times 10 \times 11 = 440\). From each state, we try transitions to the next digit \(d\) (\(10\) possibilities). Therefore, the number of loop iterations to advance one digit is at most \(440 \times 10 = 4400\). The total number of transitions is \(O(N \times 2 \times 2 \times 10 \times 11 \times 10)\), which amounts to approximately \(2.2 \times 10^7\) operations even considering constant factors, well within the time limit.
- Checking \(L\): Checking whether \(L\) is a smooth mountain path can be done in \(O(N)\).
Therefore, the overall time complexity is \(O(N)\).
Space Complexity
Since the DP transitions depend only on the state of the previous digit, the implementation can be done in-place (or by alternating between two arrays). The required array size is a constant size of \(2 \times 2 \times 10 \times 11\).
Therefore, the space complexity is \(O(1)\) (or \(O(N)\) when considering the memory for storing the input).
Implementation Notes
Negative remainders in subtraction: When computing
solve(R) - solve(L), the result of the subtraction may become negative. Depending on the programming language specifications (especially in C++ or Java), make sure to addMODbefore taking the remainder, like(ans_R - ans_L + is_L_smooth + MOD) % MOD.Distinguishing leading zeros: For example, when considering a number like
000123, if we compute the differences between leading0s as steps, it causes incorrect judgments. Always use theis_startflag to ensure that only steps after the actual number begins are computed.Source Code
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int MOD = 1000000007;
int dp[2][2][10][11];
int next_dp[2][2][10][11];
int solve(const string& S) {
int N = S.length();
memset(dp, 0, sizeof(dp));
dp[0][0][0][10] = 1;
for (int i = 0; i < N; ++i) {
memset(next_dp, 0, sizeof(next_dp));
int limit = S[i] - '0';
for (int less = 0; less < 2; ++less) {
for (int is_start = 0; is_start < 2; ++is_start) {
for (int prev_digit = 0; prev_digit < 10; ++prev_digit) {
for (int prev_diff = 0; prev_diff <= 10; ++prev_diff) {
int val = dp[less][is_start][prev_digit][prev_diff];
if (val == 0) continue;
int max_d = less ? 9 : limit;
for (int d = 0; d <= max_d; ++d) {
int next_less = less || (d < limit);
int next_is_start = is_start;
int next_prev_digit = prev_digit;
int next_prev_diff = prev_diff;
if (is_start == 0) {
if (d == 0) {
next_is_start = 0;
next_prev_digit = 0;
next_prev_diff = 10;
} else {
next_is_start = 1;
next_prev_digit = d;
next_prev_diff = 10;
}
} else {
next_is_start = 1;
next_prev_digit = d;
int diff = abs(prev_digit - d);
if (prev_diff == 10) {
next_prev_diff = diff;
} else {
if (diff >= prev_diff) {
next_prev_diff = diff;
} else {
continue;
}
}
}
next_dp[next_less][next_is_start][next_prev_digit][next_prev_diff] += val;
if (next_dp[next_less][next_is_start][next_prev_digit][next_prev_diff] >= MOD) {
next_dp[next_less][next_is_start][next_prev_digit][next_prev_diff] -= MOD;
}
}
}
}
}
}
memcpy(dp, next_dp, sizeof(dp));
}
int ans = 0;
for (int less = 0; less < 2; ++less) {
for (int prev_digit = 0; prev_digit < 10; ++prev_digit) {
for (int prev_diff = 0; prev_diff <= 10; ++prev_diff) {
ans += dp[less][1][prev_digit][prev_diff];
if (ans >= MOD) ans -= MOD;
}
}
}
return ans;
}
bool is_smooth(const string& S) {
if (S.length() <= 2) return true;
int prev_diff = -1;
for (size_t i = 0; i < S.length() - 1; ++i) {
int diff = abs((S[i] - '0') - (S[i+1] - '0'));
if (prev_diff != -1 && diff < prev_diff) {
return false;
}
prev_diff = diff;
}
return true;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string L, R;
if (!(cin >> L >> R)) return 0;
int ans_R = solve(R);
int ans_L = solve(L);
int is_L_smooth = is_smooth(L) ? 1 : 0;
int ans = (ans_R - ans_L + is_L_smooth + MOD) % MOD;
cout << ans << "\n";
return 0;
}
This editorial was generated by gemini-3.5-flash-thinking.
投稿日時:
最終更新: