E - スムーズな山道 / Smooth Mountain Path Editorial
by
kyopro_friends
この問題は桁DPにより解くことができます。
\(X\) 以下の正の整数であり「スムーズな山道」であるものの個数を \(f(X)\) とします。求める答えは、\(f(R)-f(L-1)\) ですが、\(L-1\) を計算するのは面倒なので「\(L\) がスムーズな山道であるとき \(f(R)-f(L)+1\) 、そうでないとき \(f(R)-f(L)\) 」と考えることにします。
\(L\) がスムーズな山道であるかどうかの判定は容易なので、 \(f(X)\) を高速に求めることができれば十分です。
下の桁から決めていく桁DPを考えます。
\(\mathrm{dp}_X[i][\mathrm{leq}][\mathrm{pre}][\mathrm{gap}]\) を、以下の条件を全て満たす長さ \(i\) の数字列 \(S\) の個数とします。
- \(S\) を整数列と解釈したとき、階差は単調非減少である。
- \(S\) の先頭の文字は \(\mathrm{pre}\) である。
- \(S\) の先頭の文字と2番目の文字を、それぞれ整数と解釈したときの差の絶対値は \(\mathrm{gap}\) である。
- \(S\) を整数と解釈したとき、\(X \bmod 10^i\) 以下なら \(\mathrm{leq}\) は True、そうでないなら False である
\(f(X)\) は \(X\) の桁数を \(N\) として、
\(\sum\mathrm{dp}_X[< N][*][\neq 0][*]+\sum \mathrm{dp}_X[N][\mathrm{True}][\neq 0][*]\)
となります。DPの遷移は、\(S\) の先頭に追加する文字の種類を全て試せばよいです。
\(R\) の桁数を \(N\) 、基数を \(B\) として、計算量は \(O(NB^3)\) になります。
実装例 (C++)
#include<bits/stdc++.h>
#include<atcoder/modint>
using namespace std;
using mint=atcoder::modint1000000007;
mint f(string s){
if(s.size() == 1){
return s[0] - '0';
}
map<array<int, 3>, mint> dp;
for(int i=0; i<10; i++){
dp[{i <= s.back() - '0', i, 10}] = 1;
}
mint ans=0;
for(int pos=s.size()-2; pos>=0; pos--){
int c = s[pos] - '0';
map<array<int, 3>, mint> new_dp;
for(auto[key, val]: dp){
auto[leq, pre, gap] = key;
if(pre != 0){
ans += val;
}
for(int i=0; i<10; i++){
if(abs(i-pre) <= gap){
array<int,3> new_key = {i<c || (i==c && leq), i, abs(i-pre)};
new_dp[new_key] += val;
}
}
}
swap(dp, new_dp);
}
for(auto[key, val]: dp){
auto[leq, pre, gap] = key;
if(pre != 0 && leq){
ans += val;
}
}
return ans;
}
bool is_ok(string s){
int gap=0;
for(int i=1; i<s.size(); i++){
int new_gap=abs(s[i-1]-s[i]);
if(gap>new_gap)return false;
gap=new_gap;
}
return true;
}
int main() {
string L, R;
cin >> L >> R;
mint ans = f(R) - f(L);
if(is_ok(L)) ans++;
cout << ans.val() << endl;
}
Python では多倍長整数を扱うことができるため、\(L\) がスムーズな山道かどうを判定する代わりに、直接 \(f(R)-f(L-1)\) を計算しています。
実装例 (Python)
from collections import defaultdict
MOD = 10**9+7
def f(s):
if len(s)==1:
return int(s)
dp = defaultdict(int)
for i in range(10):
dp[(i <= int(s[-1]), i, 10)]=1
ans = 0
for pos in range(len(s)-2,-1,-1):
c = int(s[pos])
new_dp = defaultdict(int)
for (leq, pre, gap), val in dp.items():
if pre != 0:
ans += val
ans += MOD
for i in range(10):
if abs(i-pre) <= gap:
key=(i<c or (i==c and leq!=0), i, abs(i-pre))
new_dp[key] += val
new_dp[key] %= MOD
dp = new_dp
for (leq, pre, gap), val in dp.items():
if pre != 0 and leq:
ans += val
ans %= MOD
return ans
L = str(int(input())-1)
R = input()
print((f(R)-f(L)) % MOD)
posted:
last update:
