Magic Numbers

(平生第一次写洛谷紫题,得好好纪念一下其实是因为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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值