数位DP入门总结

数位DP

数位DP的问题一般是统计一个范围内满足数位存在某些约束的数的个数,例如数位中含有连续的两位是 13 13 13、数位和被某个数整除、该数本身被某个数整除、二进制1的个数大于等于0的个数等等。

数位DP一般使用记忆化搜索(因为记忆化搜索处理起来比较简单),核心思想是:

从高位向低位枚举第 p o s pos pos 位,当前的状态为 s t a t u s status status,枚举这一位放置的数字,那么状态转移就是 d ( p o s , s t a t u s ) = ∑ d ( p o s − 1 , n e x t _ s t a t u s ) d(pos,status) = \sum{d(pos-1,next\_status)} d(pos,status)=d(pos1,next_status),边界值是 d ( 0 , ? ) = 1 d(0,?) = 1 d(0,?)=1

枚举当前的数位有一种特殊情况需要考虑,就是如果是状态从前缀每一位的最大值压进来,或者说是压边界的状态,那么只能枚举到 d i g i t [ p o s ] digit[pos] digit[pos] 位,例如上界是 123 123 123,当前状态为 12 ? 12? 12?,那么这时只能枚举 [ 0 , 3 ] [0,3] [0,3],否则就能枚举 [ 0 , 9 ] [0,9] [0,9]

数位DP的时间复杂度一般就是定义DP状态的各维度相乘,一般的模板如下:

注意到在记忆化的时候除了压边界的状态都会记忆化,之所以不记录压边界的状态是因为对于某位的前置状态来说,压边界导致后面的状态都会发生限制,相反不压边界后面的数位均能枚举到 9 9 9,因此压边界的状态不需要记录,对时间复杂度并无影响。

int d[len][maxn], digit[len];

int dfs(int pos, int status, bool flag) {
	if (!pos) return check(status);  //检查这时数的状态是否满足条件
	if (!flag && d[pos][status] != -1) return d[pos][status];
	int up = flag ? digit[pos] : 9;   //如果是二进制数位上界就是1
	int ans = 0;
	for (int i = 0;i <= up; i++) {
		ans += dfs(pos - 1, next_status, flag && i == up);
	}
	if (!flag) d[pos][status] = ans;
	return ans;
}

但是我们仍可以记忆化压边界的状态,只是需要增加空间复杂度,即将 d p dp dp数组设置为 d [ l e n ] [ m a x n ] [ 2 ] d[len][maxn][2] d[len][maxn][2],这样可以对所有的状态记忆化;有些问题比较复杂,比如还有前导0的限制等等,要在搜索过程中再传入一个或多个布尔变量,有时在记忆化的过程中这些变量也需要特判才能记忆化,但是如果我们将状态数组多开几维就不会有这个问题: d [ l e n ] [ m a x n ] [ 2 ] [ 2 ] . . . d[len][maxn][2][2]... d[len][maxn][2][2]...

只是这种做法的状态依赖了范围的上界,对于不同的范围处理时需要每次都初始化dp数组,而上一种做法对于相同的边界检查不需要重复初始化

int d[len][maxn][2], digit[len];

int dfs(int pos, int status, bool flag) {
	if (!pos) return status;  //检查这时数的状态是否满足条件
	if (d[pos][status][flag] != -1) return d[pos][status][flag];
	int up = flag ? digit[pos] : 9;   //如果是二进制数位上界就是1
	int ans = 0;
	for (int i = 0;i <= up; i++) {
		ans += dfs(pos - 1, next_status, flag && i == up);
	}
	return d[pos][status] = ans;
}

对于不同的题目还会有不同的细节需要处理,详见下面的例题分析。

例题分析

例题一

牛客 A|B

题目大意

给定 a , x a,x a,x,需要我们统计在 1 ≤ b ≤ x 1 \leq b \leq x 1bx范围内有多少 b b b满足 a ∣ b = a + b a|b = a+b ab=a+b

解题思路

a ∣ b = a + b a|b = a+b ab=a+b这一条件按位考虑,实际上就是对于 a a a的二进制每一位当且仅当为 0 0 0时,在 b b b中既可以出现0也可以出现1。考虑暴力从高位向低位枚举合法位置放0或者1,这样最多有 2 30 2^{30} 230种情况,但是假设当前为第 p o s pos pos位,对前缀的非压界任何状态来说,后面能放的方案数一定是相同的,因此这题就是一个最裸的数位DP,因为考虑了0最后要减一。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int d[32], digit[32];
bitset<32> b;
int a, x;

int dfs(int pos, bool flag) {
    if (pos < 0) return 1;
    if (!flag && d[pos] != -1) return d[pos];
    int up = flag ? digit[pos] : 1, ans = 0;
    for (int i = 0; i <= up; i++) {
        if (i && b[pos]) break;
        ans += dfs(pos - 1, flag && i == up);
    }
    if (!flag) d[pos] = ans;
    return ans;
}

int solve() {
    memset(d, -1, sizeof d);
    int len = 0;
    while (x) {
        digit[len++] = x & 1;
        x >>= 1;
    }
    b = bitset<32>(a);
    return dfs(len - 1, 1);
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        cin >> a >> x;
        cout << solve() - 1 << ENDL;
    }
    return 0;
}
例题二

LightOJ-1068

题目大意

给定 A , B , k A,B,k A,B,k,求出 [ A , B ] [A,B] [A,B]范围内被 k k k整除且数位之和被 k k k整除的数的个数。

解题思路

实际上这题也是裸的数位DP。设 f [ p o s ] [ r e s 1 ] [ r e s 2 ] f[pos][res1][res2] f[pos][res1][res2]表示枚举到第 p o s pos pos位,当前数的大小模 k k k r e s 1 res1 res1,当前数位之和模 k k k r e s 2 res2 res2的数的个数,其中 r e s 1 res1 res1要根据大数取模的思想更新状态,其他的根据常见思路进行状态转移即可,边界的返回条件是 r e s 1 = = 0    & &    r e s 2 = = 0 res1==0 ~~\&\& ~~res2==0 res1==0  &&  res2==0。注释掉的部分是将压界同样计入状态。

注意到 k k k可能很大,但是显然一个 i n t int int范围内的整数的数位和不可能超过 83 83 83,当 k > 83 k>83 k>83直接输出0即可。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ll f[20][105][105];
int digit[20], k;

ll dfs(int pos, int res1, int res2, bool flag) {   //d---当前的数位,res---当前的余数,flag---当前枚举的是否是上界
    if (pos == 0) return res1 == 0 && res2 == 0;
    if (!flag && f[pos][res1][res2] != -1) return f[pos][res1][res2];
    ll ans = 0;
    int up = flag ? digit[pos] : 9;
    for (int i = 0; i <= up; i++) ans += dfs(pos - 1, (res1 * 10 + i) % k, (res2 + i) % k, flag && i == up);
    if (!flag) f[pos][res1][res2] = ans;
    return ans;
}

/*
ll f[20][105][105][2];
int digit[20], k;

ll dfs(int pos, int res1, int res2, bool flag) {   //d---当前的数位,res1---数的余数,res2---数位和的余数,flag---当前枚举的是否是上界
    if (pos == 0) return res1 == 0 && res2 == 0;
    if (f[pos][res1][res2][flag] != -1) return f[pos][res1][res2][flag];
    ll ans = 0;
    int up = flag ? digit[pos] : 9;
    for (int i = 0; i <= up; i++) ans += dfs(pos - 1, (res1 * 10 + i) % k, (res2 + i) % k, flag && i == up);
    return f[pos][res1][res2][flag] = ans;
}
*/

ll solve(int x) {
    int pos = 0;
    while (x) {
        digit[++pos] = x % 10;
        x /= 10;
    }
    return dfs(pos, 0, 0, 1);
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T, kase = 0;
    int a, b;
    cin >> T;
    while (T--) {
        cin >> a >> b >> k;
        cout << "Case " << ++kase << ": ";
        if (k > 83) cout << 0 << ENDL;
        else {
            memset(f, -1, sizeof f);
            cout << solve(b) - solve(a - 1) << ENDL;
        }
    }
    return 0;
}
例题三

AcWing 311

题目大意

如果一个数能被它的各位数字之和整除,则称这个数是"月之数",统计 [ l , r ] [l,r] [l,r]的月之数个数。

解题思路

因为一个数的大小不可能定义在状态之中,因此必须从其他方面考虑;反过来想,如果我们已经知道了一个数的数位之和 k k k,只需要统计数位和为 k k k的数中有多少能被 k k k整除,这样可以通过同余状态进行转移,和例题二类似。因此枚举 k ∈ [ 1 , 90 ] k \in [1,90] k[1,90],做这么多次数位DP累加求和即可。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int d[15][90][90];
int digit[15];
int k;

int dfs(int pos, int sum, int res, bool flag) {
    if (!pos) return sum == k && res == 0;
    if (!flag && d[pos][sum][res] != -1) return d[pos][sum][res];
    int up = flag ? digit[pos] : 9, ans = 0;
    for (int i = 0; i <= up; i++) ans += dfs(pos - 1, sum + i, (res * 10 + i) % k, flag && i == up);
    if (!flag) d[pos][sum][res] = ans;
    return ans;
}

int cal(int x) {
    int len = 0;
    while (x) {
        digit[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 0, 1);
}


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int l, r, ans = 0;
    cin >> l >> r;
    for (int i = 1; i < 90; i++) {
        memset(d, -1, sizeof d);
        k = i;
        ans += cal(r) - cal(l - 1);
    }
    cout << ans << endl;
    return 0;
}
例题四

HDU-3652

题目大意

定义 B − n u m b e r B-number Bnumber为一个能被13整除非负整数且数位中存在连续的两位是"13",求出 [ 1 , n ] [1,n] [1,n] B − n u m b e r B-number Bnumber的数目。

解题思路

实际上这种题型也较为常见,线性的计数DP也经常有这种“约束”来计数,大致的思想都是一致的:

d [ p o s ] [ r e s ] [ 0 / 1 / 2 ] d[pos][res][0/1/2] d[pos][res][0/1/2]代表当前枚举到第 p o s pos pos位,对 13 13 13取模为 r e s res res,前面不含有"13"/前一位是"1"/前面已经含有"13"的方案数目。然后根据常规数位DP进行状态转移即可。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int d[20][15][3], digit[15];
const int k = 13;

int dfs(int pos, int res, int st, bool flag) {       //0 -- 不合法, 1 -- 前一位是1, 2 -- 已经包含了13
    if (!pos) return res == 0 && st == 2;
    if (!flag && d[pos][res][st] != -1) return d[pos][res][st];
    int up = flag ? digit[pos] : 9, ans = 0;
    for (int i = 0; i <= up; i++) {
        if (st == 2) ans += dfs(pos - 1, (res * 10 + i) % k, 2, flag && i == up);
        else if (st == 1) {
            if (i == 3) ans += dfs(pos - 1, (res * 10 + i) % k, 2, flag && i == up);
            else if (i == 1) ans += dfs(pos - 1, (res * 10 + i) % k, 1, flag && i == up);
            else ans += dfs(pos - 1, (res * 10 + i) % k, 0, flag && i == up);
        } else {
            if (i == 1) ans += dfs(pos - 1, (res * 10 + i) % k, 1, flag && i == up);
            else ans += dfs(pos - 1, (res * 10 + i) % k, 0, flag && i == up);
        }
    }
    if (!flag) d[pos][res][st] = ans;
    return ans;
}

int cal(int x) {
    int len = 0;
    while (x) {
        digit[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 0, 1);
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    memset(d, -1, sizeof d);
    while (scanf("%d", &n) != EOF) {
        printf("%d\n", cal(n));
    }
    return 0;
}
例题五

洛谷 P2657

题目大意

不含前导零且相邻两个数字之差至少为 2 2 2 的正整数被称为 w i n d y windy windy 数。在 a a a b b b 之间,包括 a a a b b b ,总共有多少个 w i n d y windy windy 数?

解题思路

题目所说的不含前导0的意思是前导0不计入相邻两数字之差,也就是说我们要看当前枚举的数位前面是否含有前导0,因此增加一个布尔变量记录,在状态转移时判断当前的状态是否含有前导0然后根据枚举的数位转移到下一个状态。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ll d[20][20];
int digit[20];

ll dfs(int pos, int pre, bool flag, bool zero) {
    if (!pos) return 1;
    if (!flag && d[pos][pre] != -1) return d[pos][pre];
    int up = flag ? digit[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; i++) {
        if (zero) {
            if (i == 0) ans += dfs(pos - 1, -1, 0, 1);
            else ans += dfs(pos - 1, i, flag && i == up, 0);
        } else if (abs(i - pre) >= 2) ans += dfs(pos - 1, i, flag && i == up, 0);
    }
    if (!flag) d[pos][pre] = ans;
    return ans;
}

ll cal(int x) {
    int len = 0;
    while (x) {
        digit[++len] = x % 10;
        x /= 10;
    }
    memset(d, -1, sizeof d);
    return dfs(len, -1, 1, 1);
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int a, b;
    cin >> a >> b;
    cout << cal(b) - cal(a - 1) << ENDL;
    return 0;
}
例题六

POJ-3252

题目大意

求出 [ l , r ] [l,r] [l,r]范围内满足二进制分解后0的个数大于等于1的个数这样的数的个数。

解题思路

同样需要注意前导0的数位DP,显然搜索过程中如果含有前导0那么0不应该被计数,只有转化为不含前导0的状态后才可以计数。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>


using namespace std;
#define ENDL "\n"
typedef long long ll;
const int maxn = 2e5 + 10;

int d[35][35][35];
int digit[35];

//如果前面全部选0那么不应该计入0的个数,只有选了1之后的0才会计数
int dfs(int pos, int sum1, int sum2, bool pre, bool flag) {
    if (!pos) return !pre && sum2 >= sum1;
    if (!flag && d[pos][sum1][sum2] != -1) return d[pos][sum1][sum2];
    int up = flag ? digit[pos] : 1, ans = 0;
    for (int i = 0; i <= up; i++) {
        if (pre) ans += i == 1 ? dfs(pos - 1, 1, 0, 0, flag && i == up) : dfs(pos - 1, 0, 0, 1, flag && i == up);
        else ans += dfs(pos - 1, sum1 + (i == 1), sum2 + (i == 0), pre, flag && i == up);
    }
    if (!flag) d[pos][sum1][sum2] = ans;
    return ans;
}

int cal(int x) {
    int len = 0;
    while (x) {
        digit[++len] = x & 1;
        x >>= 1;
    }
    return dfs(len, 0, 0, 1, 1);
}

int main() {
    int x, y;
    scanf("%d%d", &x, &y);
    memset(d, -1, sizeof d);
    printf("%d\n", cal(y) - cal(x - 1));
    return 0;
}
例题七

洛谷 P2602

题目大意

给定两个正整数 a a a b b b,求在 [ a , b ] [a,b] [a,b] 中的所有整数中,每个数码( d i g i t digit digit )各出现了多少次。

解题思路

这个题和例一一样,虽然不难但是状态的定义比较难想到(或者说和其他例题的状态定义不太相同)

我们可以将问题变成,给定数码 c u r cur cur,求出其在 [ a , b ] [a,b] [a,b]中出现过多少次,假设填到了第 p o s pos pos位时已经填了 x x x c u r cur cur,那么最终的答案一定是 ∑ i = 1 u p d ( p o s − 1 , x + ( i = = c u r ) ) \sum_{i = 1}^{up}{d(pos-1,x + (i==cur))} i=1upd(pos1,x+(i==cur))也就是说在计数时每次对于前缀填了若干个数位后后面的状态都是相同的,而且每次枚举的状态都不会重复,虽然看似比较复杂且状态不好定义,但实际上是比较裸的数位DP。

本题的难点是对于前缀0的处理,因为本题不是求出符合条件的数个数,而是求每个数每种数码出现的次数,在统计0时显然前导0不应该被计入,记忆化时要特判是否含有前导0,这个我也想不清楚。但是如果我们将布尔标记的状态都定义在状态转移的数组中,就可以不用关心这些边界而转移了,详见注释部分。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ll d[20][20];
int digit[20];
int cur;

ll dfs(int pos, int sum, bool pre, bool flag) {
    if (!pos) return sum;
    if (!flag && !pre && d[pos][sum] != -1) return d[pos][sum];
    int up = flag ? digit[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; i++) {
        if (pre) ans += (i == 0) ? dfs(pos - 1, 0, 1, flag && i == up) : dfs(pos - 1, i == cur, 0, flag && i == up);
        else ans += dfs(pos - 1, sum + (i == cur), 0, flag && i == up);
    }
    if (!flag && !pre) d[pos][sum] = ans;
    return ans;
}

/*
ll d[20][20][2][2];
int digit[20];
int cur;

ll dfs(int pos, int sum, bool pre, bool flag) {
    if (!pos) return sum;
    if (d[pos][sum][pre][flag] != -1) return d[pos][sum][pre][flag];
    int up = flag ? digit[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; i++) {
        if (pre) ans += (i == 0) ? dfs(pos - 1, 0, 1, flag && i == up) : dfs(pos - 1, i == cur, 0, flag && i == up);
        else ans += dfs(pos - 1, sum + (i == cur), 0, flag && i == up);
    }
    return d[pos][sum][pre][flag] = ans;
}
*/

ll cal(ll x) {
    int len = 0;
    while (x) {
        digit[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 1, 1);
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
//    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    ll a, b;
    cin >> a >> b;
    for (int i = 0; i <= 9; i++) {
        cur = i;
        memset(d, -1, sizeof d);
        cout << cal(b) - cal(a - 1) << " ";
    }
    return 0;
}
例题八

Codeforces 628D

题目大意

定义一个数字是 d − m a g i c d-magic dmagic,当且仅当数码 d d d 只出现在该数字的偶数的数位上(从左到右依次为 [ 1 , l e n ] [1,len] [1,len])。现在需要我们找出 [ l , r ] [l,r] [l,r]范围内能被 m m m 整除的 d − m a g i c d-magic dmagic 数的个数。

解题思路

对于范围比较大的数位DP,只需要输入字符串,然后转化为整形数组,我习惯从高位处理到低位的写法,对于长度为奇数的字符串,其倒置后偶数的位置刚好相同;对于长度为偶数的字符串,其偶数位置刚好对应倒置的奇数位置。如果偶数位置的上界小于 d d d,直接返回0。最后一点需要注意的是可能0也会被看做一种情况( d ≠ 0 d \neq 0 d=0),设置一个变量判断是否在偶数为填了至少一个 d d d

处理答案时为了防止做大数减一,将左边界减去后然后特判左边界是否符合情况即可。

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2020;

char a[maxn], b[maxn];
ll f[2020][2020];
int digit[maxn];
int k, d;
bool odd;

ll dfs(int pos, int res, bool vis, bool flag) {
    if (!pos) return res == 0;
    if (!flag && f[pos][res] != -1) return f[pos][res];
    int up = flag ? digit[pos] : 9;
    ll ans = 0;
    bool ok = odd ? (!(pos & 1)) : (pos & 1);
    if (ok) {
        if (up < d) {
            if (!flag) return f[pos][res] = 0;
            return 0;
        } else ans = dfs(pos - 1, (res * 10 + d) % k, 1, flag && d == up);
    } else {
        for (int i = 0; i <= up; i++) {
            if (i == d) continue;
            ans = (ans + dfs(pos - 1, (res * 10 + i) % k, vis, flag && i == up)) % Mod;
        }
    }
    if (!flag) f[pos][res] = ans;
    return ans;
}

int check(char *s) {
    int len = strlen(s + 1), res = 0;
    for (int i = 1; i <= len; i++) {
        if ((i & 1) && s[i] - '0' == d) return 0;
        if (!(i & 1) && s[i] - '0' != d) return 0;
        res = (res * 10 + s[i] - '0') % k;
    }
    return res == 0;
}

ll cal(char *s) {
    int len = strlen(s + 1);
    for (int i = 1, j = len; i <= len; i++, j--) digit[j] = s[i] - '0';
    odd = len & 1;
    return dfs(len, 0, 0, 1);
}


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> k >> d;
    scanf("%s", a + 1);
    scanf("%s", b + 1);
    memset(f, -1, sizeof f);
//    cout << cal(b) << endl;
//    cout << check(a) << endl;
    printf("%lld\n", (cal(b) - cal(a) + check(a) + Mod) % Mod);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值