题目链接:
https://codeforces.com/problemset/problem/55/D
题意:
找到区间范围中的数 的数量,要求 ,能被它每一个非零的数位整除。
思路:
如果一个数能被它所有非零位数整除,那么它一定能被所有非零位数的最小公倍数整除,所以记忆化搜索的过程中要记录第 位之前的所有非零位数的最小公倍数。
接着看余数,因为 1 ~ 9 的最小公倍数为 2520,所以可以将数先对 2520 进行一个取余。这样子就可以定义 数组 表示长度为 的数字,第 位之前所有的数字的最小公倍数为 ,对 2520 取余后余数为 的数字有多少。数组大小为 20 * 2520 * 2520,超内存了。
因为 1 ~ 9 的数字的最小公倍数只有 48 种,所以可以通过离散化的方式让数组大小变成 20 * 50 * 2520。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL dp[20][50][2520], num[20], Hash[2520];
LL dfs(int pos, int limit, int prelcm, int res){
if (pos == 0) return res % prelcm == 0;
if (!limit && dp[pos][Hash[prelcm]][res] != -1) return dp[pos][Hash[prelcm]][res];
LL ans = 0;
int up = (limit ? num[pos] : 9);
for (int i = 0; i <= up; i ++ ){
int t = prelcm;
if (i) t = lcm(prelcm, i);
ans += dfs(pos - 1, limit && (i == num[pos]), t, (res * 10 + i) % 2520);
}
if (!limit) dp[pos][Hash[prelcm]][res] = ans;
return ans;
}
LL solve(LL x){
int len = 0;
while(x){
num[++ len] = x % 10;
x /= 10;
}
return dfs(len, 1, 1, 0);
}
void solve(){
memset(dp, -1, sizeof dp);
LL a, b;
cin >> a >> b;
cout << solve(b) - solve(a - 1) << "\n";
}
void init(){
int cnt = 0;
for (int i = 1; i <= 2520; i ++ )
if (2520 % i == 0)
Hash[i] = ++ cnt;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
init();
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
分类:
数学-number theory
, 动态规划-数位dp
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】