题解: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;
}
posted @ 2025-09-16 19:49  yanbinmu  阅读(5)  评论(0)    收藏  举报