SP8177-JZPEXT题解

原题链接

思路简介

很显然,这是一道数位 dp 题目。对于这一类题目,我们可以采用记忆化搜索的方法进行处理。这种方法套路性较强,易于掌握。不难得到,要是这个数能被每一位上的数整除,它一定能被这些数的最小公倍数整除。我们可以将 dp 数组设为三维,dpi,j,k 表示目前在数字的第 i 位,当前数字为 j,每一位数字的最小公倍数为 k 的满足条件的数字个数。但是 1l,r1018,显然空间会爆炸,我们考虑如何对其进行优化。

我们注意到这个数只需要能够被每一位上的数整除即可,而 19 的最小公倍数为 2520,那么我们可以在每一次更新数时将这个数对 2520 取模,不会对判定产生影响。

但是,开一个 20×2520×2520 的数组在空间上仍然是不可接受的,我们考虑进一步优化。注意到不同的个位数能够得到的最小公倍数数量是极其有限的,打表可知只有 48 个。那么我们可以将每个最小公倍数与一个编号一一对应,用一个数组或是 map 存储即可。

注意两个坑点:

  1. 在进行记忆化搜索时,最开始的最小公倍数要设为 1 而不是 0,否则会得到 RE 的好结果。
  2. 这道题对代码长度有限制,尽可能写的短一些。

代码

#include <bits/stdc++.h>
typedef long long i64;
const int Mod = 2520;
int T, tot, a[20];
i64 f[20][2600][50], id[2600], l, r;
inline int lcm(int a, int b) { return a * b / std::__gcd(a, b); }
inline i64 dfs(int pos, int num, int mod, bool limit) {
    if (!pos) return (num % mod == 0);
    if (!limit && ~f[pos][num][id[mod]]) return f[pos][num][id[mod]];
    int up = limit ? a[pos] : 9;
    i64 res = 0;
    for(int i = 0; i <= up; ++i) res += dfs(pos - 1, (num * 10 + i) % Mod, i ? lcm(mod, i) : mod, limit && i == up);
    if (!limit) f[pos][num][id[mod]] = res;
    return res;
}
inline i64 solve(i64 num) {
    int len = 0;
    while (num) a[++len] = num % 10, num /= 10;
    return dfs(len, 0, 1, 1);
}
int main() {
    std::cin >> T;
    memset(f, -1, sizeof(f));
    for(int i = 1; i <= Mod; ++i) if (!(Mod % i)) id[i] = ++tot;
    while (T--) {
        std::cin >> l >> r;
        std::cout << solve(r) - solve(l - 1) << '\n';
    }
    return 0;
}
posted @   Floze3  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示