(平生第一次写洛谷紫题,得好好纪念一下其实是因为debug半天突然ac了想发博客)
一,题目
# Magic Numbers
## 题面翻译
给你 $4$ 个数 $m,d,l,r$ ,保证 $l,r$ 位数相同。
问满足以下条件的数 $x$ 的个数:
1. $l \leq x\leq r$
2. $x$ 的偶数位是 $d$,奇数位不是 $d$。 (这里定义偶数位为从高位往低位的数的偶数位)
3. $m|x$
答案对 $1000000007$ 取模。
$1\le m \le 2000,0\le d \le 9,1\le l \le r \le 10^{2000}$
## 题目描述
Consider the decimal presentation of an integer. Let's call a number d-magic if digit $ d $ appears in decimal presentation of the number on even positions and nowhere else.
For example, the numbers $ 1727374 $ , $ 17 $ , $ 1 $ are 7-magic but $ 77 $ , $ 7 $ , $ 123 $ , $ 34 $ , $ 71 $ are not 7-magic. On the other hand the number $ 7 $ is 0-magic, $ 123 $ is 2-magic, $ 34 $ is 4-magic and $ 71 $ is 1-magic.
Find the number of d-magic numbers in the segment $ [a,b] $ that are multiple of $ m $ . Because the answer can be very huge you should only find its value modulo $ 10^{9}+7 $ (so you should find the remainder after dividing by $ 10^{9}+7 $ ).
## 输入格式
The first line contains two integers $ m,d $ ( $ 1<=m<=2000 $ , $ 0<=d<=9 $ ) — the parameters from the problem statement.
The second line contains positive integer $ a $ in decimal presentation (without leading zeroes).
The third line contains positive integer $ b $ in decimal presentation (without leading zeroes).
It is guaranteed that $ a<=b $ , the number of digits in $ a $ and $ b $ are the same and don't exceed $ 2000 $ .
## 输出格式
Print the only integer $ a $ — the remainder after dividing by $ 10^{9}+7 $ of the number of d-magic numbers in segment $ [a,b] $ that are multiple of $ m $ .
## 样例 #1
### 样例输入 #1
```
2 6
10
99
```
### 样例输出 #1
```
8
```
## 样例 #2
### 样例输入 #2
```
2 0
1
9
```
### 样例输出 #2
```
4
```
## 样例 #3
### 样例输入 #3
```
19 7
1000
9999
```
### 样例输出 #3
```
6
```
## 提示
The numbers from the answer of the first example are $ 16 $ , $ 26 $ , $ 36 $ , $ 46 $ , $ 56 $ , $ 76 $ , $ 86 $ and $ 96 $ .
The numbers from the answer of the second example are $ 2 $ , $ 4 $ , $ 6 $ and $ 8 $ .
The numbers from the answer of the third example are $ 1767 $ , $ 2717 $ , $ 5757 $ , $ 6707 $ , $ 8797 $ and $ 9747 $ .
链接:Magic Numbers - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
二,思路
1,大体方向,通过数位dp来记忆化搜索所有满足条件的数的个数,基本就是正常的数位dp代码记忆化搜索里for循环的判断是否递归的代码改一下,改成如果当前搜索的数如果仍然使得偶数位是d,奇数位不是d,就继续搜索,其他的基本不变。
int dfs(int pos, int sum, int limit)
{
if (pos >= tmp.size()) return sum == 0;
if (!limit && book[pos][sum] != -1) return book[pos][sum];
int maxn = limit ? tmp[pos] - '0' : 9, ans = 0;
for (int i = 0; i <= maxn; ++i)
{
if ((pos % 2 == 0 && i == d) || pos % 2 == 1 && i != d)
ans = (ans + dfs(pos + 1, (sum * 10 + i) % m, limit && i == maxn)) % mod;
}
if (!limit) book[pos][sum] = ans % mod;
return ans % mod;
}
2,坑点和要解决的问题:
(1)不同于往常的从低位到高位找,这题要从高位到低位找,如果习惯于写低到高找的数位dp代码,在这里很容易出错。
(2)题目给的数据范围很大,整形和长整型不能储存a和b,只能用字符串,写的时候容易忘记减去‘0’。
(3)由于用的是字符串,下面这行代码中的a不好减去1,写的时候,容易忘记写对于a的讨论的部分。
(注:写dfs的时候千万不要传字符串进去,不然你连怎么超时都不知道,不要问我为什么)
int ans = (handle(b) - handle(a) + mod) % mod;
三,代码
#define _CRT_SECURE_NO_WARNINGS 1
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<math.h>
#include<cmath>
#include<string>
#include<unordered_map>
#include<set>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
#define endl '\n'
#define int long long
const int mod = 1e9 + 7;
int m, d;
string a, b, tmp;
signed book[3005][30005] = { 0 };
int dfs(int pos, int sum, int limit)
{
if (pos >= tmp.size()) return sum == 0;
if (!limit && book[pos][sum] != -1) return book[pos][sum];
int maxn = limit ? tmp[pos] - '0' : 9, ans = 0;
for (int i = 0; i <= maxn; ++i)
{
if ((pos % 2 == 0 && i == d) || pos % 2 == 1 && i != d)
ans = (ans + dfs(pos + 1, (sum * 10 + i) % m, limit && i == maxn)) % mod;
}
if (!limit) book[pos][sum] = ans % mod;
return ans % mod;
}
int handle(string str)
{
tmp = str;
return dfs(1, 0, 1);
}
signed main()
{
memset(book, -1, sizeof(book));
ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
cin >> m >> d >> a >> b;
a = '0' + a, b = '0' + b;
int ans = (handle(b) - handle(a) + mod) % mod;
int flag = 0, num = 0;
for (int i = 1; i < a.size(); ++i)
{
num = (num * 10 + a[i] - '0') % m;
if ((i % 2 != 0 && a[i] - '0' == d) || (i % 2 == 0 && a[i] - '0' != d))
flag = 1;
}
if (flag == 0 && num == 0) cout << ans + 1;
else cout << ans;
return 0;
}