首页 > 代码库 > [补档计划] 数位dp

[补档计划] 数位dp

  数位dp 就是用 Dynamic Programming 解决 数位统计问题 .

[CF55D] Beautiful numbers

题意

  求区间 [L, R] 中满足每个非零的数位都整除原数的数的个数.

  $1\le L, R\le 9\times {10}^{18}$ .

分析

  记 L 为 x 的数位的 LCM. 每个非零的数位都整数原数的数 x , 当且仅当 $x\equiv 0(\mod L)$ . 由于 $L | 2520$, 所以当且仅当 $x\mod 2520\equiv 0(\mod L)$ .

  数位dp. f[pos][div][x] 表示数位不超过 pos 位, 前缀数位的 LCM 为 div , 前缀数位的和 mod 2520 为 x, 最终符合条件的数的个数.

实现

递推

技术分享
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>

#define F(i, a, b) for (register int i = (a); i <= (b); i++)

#define LL long long

LL f[25][50][3000]; int id[3000], tot, rid[50];
int t; LL L, R;
int bit[25], len;

inline LL rd(void) {
    LL f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == -) f = -1;
    LL x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-0; return x*f;
}

inline LL gcd(LL a, LL b) { return !b ? a : gcd(b, a%b); }
inline LL LCM(LL a, LL b) { return !a || !b ? a+b : a / gcd(a, b) * b; }
inline void Transfer(int &div, int &sum, int d) {
    div = LCM(div, d);
    sum = (sum * 10 + d) % 2520;
}
LL Solve(LL x) {
    x++;
    while (len > 0) bit[len--] = 0;
    for (; x > 0; x /= 10) bit[++len] = x%10;

    LL res = 0;
    for (int d = 0; d < bit[len]; d++)
        res += f[len-1][id[LCM(1, d)]][d];
    for (int pos = len-1, div = bit[len], sum = bit[len]; pos >= 1; pos--) {
        for (int d = 0; d < bit[pos]; d++) {
            int _div = div, _sum = sum;
            Transfer(_div, _sum, d);
            res += f[pos-1][id[_div]][_sum];
        }
        Transfer(div, sum, bit[pos]);
    }
    return res;
}

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("CF55D.in", "r", stdin);
        freopen("CF55D.out", "w", stdout);
    #endif

    for (int i = 1; i <= 2520; i++)
        if (2520 % i ==0) {
            rid[ id[i] = ++tot ] = i;
            for (int j = 0; j < 2520; j += i)
                f[0][tot][j] = 1;
        }
    F(pos, 1, 20)
        F(divID, 1, tot)
            for (int sum = 0; sum < 2520; sum++)
                F(d, 0, 9) {
                    int _div = rid[divID], _sum = sum;
                    Transfer(_div, _sum, d);
                    f[pos][divID][sum] += f[pos-1][id[_div]][_sum];
                }

    t = rd();
    F(c, 1, t) {
        L = rd(), R = rd();
        LL ansR = Solve(R);
        printf("%I64d\n", ansR - Solve(L-1));
    }

    return 0;
}
View Code

记忆化

技术分享
#include <cstdio>
#include <cstring>
#include <cstdlib>

#define F(i, a, b) for (register int i = (a); i <= (b); i++)

#define divID (id[div])

#define LL long long

int id[3000], tot;
int t; LL L, R;
int bit[20], len;
LL f[20][50][3000];

inline LL gcd(LL a, LL b) { return !b ? a : gcd(b, a % b); }
inline LL LCM(LL a, LL b) { return a / gcd(a, b) * b; }
LL DFS(int pos, int div, int sum, bool lim) {
    if (!pos) return sum % div == 0;
    if (!lim && ~f[pos][divID][sum]) return f[pos][divID][sum];
    LL res = 0; int num = (lim ? bit[pos] : 9);
    F(d, 0, num) {
        int _div = (d == 0 ? div : LCM(div, d));
        int _sum = (sum * 10 + d) % 2520;
        res += DFS(pos-1, _div, _sum, lim && (d == num));
    }
    if (!lim) f[pos][divID][sum] = res;
    return res;
}
LL Solve(LL x) {
    while (len > 0)
        bit[len--] = 0;
    for (; x > 0; x /= 10)
        bit[++len] = x % 10;
    return DFS(len, 1, 0, true);
}

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("CF55D.in", "r", stdin);
        freopen("CF55D.out", "w", stdout);
    #endif

    for (int i = 1; i <= 2520; i++)
        if (2520 % i == 0)
            id[i] = ++tot;

    memset(f, -1, sizeof f);
    scanf("%d", &t);
    F(c, 1, t) {
        scanf("%I64d %I64d", &L, &R);
        LL ansR = Solve(R);
        printf("%I64d\n", ansR - Solve(L-1));
    }

    return 0;
}
View Code

 

[补档计划] 数位dp