题解:P11557 [ROIR 2016 Day 2] 有趣数字题解
观察题目不难发现 \(1 \leq L \leq R \leq 10^{100}\) 。
这启发我们时间复杂度可能会和位数有关,否则就炸了……
当然也有可能是个什么 \(\log\) 的……
我们考虑如果按位枚举每一位的数,那么时间复杂度将是 \(O(\text{答案})\) 的。但是如果不考虑上下界,我们就可以将某一步的答案记录下来。
比如我如果令 f[a][b] 为枚举到第 \(\text{a}\) 位,前一位是 \(\text{b}\),后面几位的方案数,那如果有相同的情况出现时,我就可以直接用了。
举个例子,比如这两个后四位的方案数是一样的:
-
1234_ _ _ _ -
2334_ _ _ _
那我们怎么处理有上下界?
我们先考虑有一条上界(如果可行,那我们可以将上下界转化为一个 \([0, \text{r}]\) 的方案数减去 \([0, \text{l})\) 的方案数)
我们可以记录当前的数是否卡在上界, 如果前面不是所有位都卡在上界上,那我就可以随便放;如果卡在上界上,那我就需要枚举所有的数。
那这个时间复杂度为什么是对的?
首先我没有处理出来记忆化的内容时,我确实要枚举到底层(最低位),但某一位的处理出来了后,前一位的复杂度就只有 \(O(10)\) 。
考虑如果卡在上界上,这个上界只有一个。如果不在上界上了,那就可以直接用记忆好的答案了。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int Mod = 1e9 + 7;
int f[105][105], digit[105];
int dfs(int pos,int lead, int pre, int limit) {
if(pos == 0) return 1;
if(!limit && f[pos][pre] != -1 && !lead) return f[pos][pre];
int up = limit ? digit[pos] : 9;
int cnt = 0;
for(int i = pre;i <= up;i ++ ) {
cnt += dfs(pos - 1, lead && i == 0, i, limit && (i == up));
cnt %= Mod;
}
if(!limit && !lead) f[pos][pre] = cnt;
return cnt % Mod;
}
int sep(string x){
memset(f, -1, sizeof(f));
memset(digit, 0, sizeof(digit));
int tot = x.size(), cnt = 0;
for(int i = tot - 1;i >= 0;i -- ) {
digit[ ++ cnt] = x[i] - '0';
}
return dfs(cnt, 1, 0, 1);
}
signed main() {
string l, r;
cin >> l >> r;
int siz = l.size(), flag = 1;
for(int i = 1;i < siz;i ++ ) {
if(l[i] < l[i - 1]) {
flag = 0;
break;
}
}
cout << (sep(r) - sep(l) + flag + Mod) % Mod;
return 0;
}

浙公网安备 33010602011771号