题目链接:点击打开链接
题意:给一个m一个d, 一个字符串a和b, 问在[a,b]范围内, 有多少个可以整除m的魔法数, 魔法数的定义是, 偶数位上都是d, 奇数位上都不是d。
思路:据说是典型的数位DP, 以前没做过数位DP, 感觉和DP差不多?
用d[i][j][p]表示当前到了第i位, 余数为j, p == 1表示目前和b串相等, p == 0表示已经比b串小了。 每次枚举第i位上放的数, 转移就行了。
区间也好弄, 我们可以先处理出小于等于b的所有情况ans1, 小于等于a的所有情况ans2, 那么ans1 - ans2, 别忘了, 还要看看a是不是符合条件,如果符合要加上。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 2000 + 10;
int T,n,m,dd,d[maxn][maxn][2],vis[maxn][maxn][2],kase=0;
inline int add(int& a, int b) {
a += b;
if(a >= mod) a -= mod;
}
char a[maxn], b[maxn];
bool ok() {
int res = 0;
for(int i=1;i<=n;i++) {
int v = a[i] - '0';
res = (res * 10 + v) % m;
if(i & 1) {
if(v == dd) return false;
}
else {
if(v != dd) return false;
}
}
if(!res) return true;
return false;
}
int dp(int i, int j, int p, char s[]) {
int& ans = d[i][j][p];
if(i == n+1) return j == 0;
if(vis[i][j][p] == kase) return ans;
vis[i][j][p] = kase;
ans = 0;
if(i & 1) {
for(int k=0;k<=9;k++) {
if(k == dd) continue;
if(!p) add(ans, dp(i+1, (j*10 + k)%m, 0, s));
else {
if(k == s[i]-'0') add(ans, dp(i+1, (j*10+k)%m, 1, s));
else if(k < s[i] - '0') add(ans, dp(i+1, (j*10+k)%m, 0, s));
}
}
}
else {
if(!p) add(ans, dp(i+1, (j*10+dd)%m, 0, s));
else {
if(dd == s[i]-'0') add(ans, dp(i+1, (j*10+dd)%m, 1, s));
else if(dd < s[i]-'0') add(ans, dp(i+1, (j*10+dd)%m, 0, s));
}
}
return ans;
}
int main() {
scanf("%d%d%s%s",&m,&dd,a+1,b+1);
n = strlen(b+1);
++kase;
int ans1 = dp(1, 0, 1, b);
++kase;
n = strlen(a+1);
int ans2 = dp(1, 0, 1, a);
if(ok()) --ans2;
int ans = (ans1 - ans2 + mod) % mod;//取模之后的两个数相减的处理
printf("%d\n",ans);
return 0;
}