又一道按位DP水题

时间:2021-11-12 09:29:00

Description

求区间[a,b]包含1的数量。例如区间[111,112], 整个区间包含两个数,分别为111,112,111包含3个1,而112包含2个1,所以区间[111,112]总共包含5个1.

Input

多组测试数据。

每组测试数据包含两个整数a, b, 1 <= a <= b <= 10^18.

Output

每组测试输出一行,表示1的数量,结果mod 10^9+7.

Sample Input

111 112
1 1000

Sample Output

5
301
 
 
代码如下:
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;

const int MOD = int(1e9)+7;
typedef long long LL;
LL f[20]; // f[i]表示还剩下i位,包含1的个数
int bit[20];
LL num;

LL _pow(int b) {
    LL ret = 1;
    for (int i = 0; i < b; ++i) {
        ret *= 10;
    }
    return ret;
}

LL dfs(int p, int e) {
    if (p == -1) return 0;
    if (!e && f[p]!=-1) return f[p];
    LL ret = 0;
    int LIM = e ? bit[p] : 9;
    for (int i = 0; i <= LIM; ++i) {
        if (i == 1) {
            ret += dfs(p-1, e&&(i==LIM));
            ret %= MOD;
            if (e&&(i==LIM)) {
                ret += (num % _pow(p) + 1)%MOD;
                ret %= MOD;
            } else {
                ret += _pow(p) % MOD;
                ret %= MOD;
            }
        } else {
            ret += dfs(p-1, e&&(i==LIM));
        }
    }
    if (!e) {
        f[p] = ret;
    }
    return ret;
}

int cal(LL x) {
    if (!x) return 0;
    num = x;
    int idx = 0;
    while (x) {
        bit[idx++] = x % 10;
        x /= 10;
    }
    return dfs(idx-1, 1);
}

int main() {
    LL a, b;
    memset(f, 0xff, sizeof (f));
    while (scanf("%lld %lld", &a, &b) != EOF) {
        printf("%d\n", (cal(b)-cal(a-1)+MOD)%MOD);
    }
    return 0;
}