1. 程式人生 > >[補檔計劃] 數位dp

[補檔計劃] 數位dp

lap std blog getch 技術 mic close 區間 turn

  數位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