随笔 - 216,  文章 - 0,  评论 - 17,  阅读 - 25535

题目链接:

https://codeforces.com/problemset/problem/55/D

题意:

找到区间范围中的数 x 的数量,要求 x,能被它每一个非零的数位整除。

思路:

如果一个数能被它所有非零位数整除,那么它一定能被所有非零位数的最小公倍数整除,所以记忆化搜索的过程中要记录第 i 位之前的所有非零位数的最小公倍数。
接着看余数,因为 1 ~ 9 的最小公倍数为 2520,所以可以将数先对 2520 进行一个取余。这样子就可以定义 dp 数组 dp[i][j][k] 表示长度为 i 的数字,第 i 位之前所有的数字的最小公倍数为 j,对 2520 取余后余数为 k 的数字有多少。数组大小为 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;
}
posted on   Hamine  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示