[補檔計劃] 數位dp
阿新 • • 發佈:2017-07-05
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 fView Code= 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 voidTransfer(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; }
記憶化
#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