[codeforces] Beautiful numbers (数位dp好题)

[codeforces] Beautiful numbers

题目链接


大致题意:

统计区间[l,r]内的数能整除它所有位上的非零整数的个数


解题思路:

如果一个数能整除它所有位上的非零整数,那么它一定可以整除它所有位上非零整数的最小公倍数

因此我们需要参数prelcm,用来记录前面数的最小公倍数

但是我们不可能记录这个数字本事是多少,因为它太大了,我们需要把它缩小

通过计算可以知道1~9的最小公倍数是2520,假设数字x可以整除它的所有位上的非零整数

那么,x%lcm(a[i])=0

然后2520%lcm(a[i])=0,这是肯定的

所以x%2520%lcm(a[i])=0,也一定成立,这样我们就把很大的数缩小到了2520

定义状态方程:f[i] [prelcm] [yu] 表示到第i位,前面数字的最小公倍数是prelcm,余数是yu的满足条件的数字个数

但是prelcm<=2520 ,yu<=2520,又超内存

我们只能想办法把prelcm缩小范围

又通过计算可以知道,1~9组成的最小公倍数只有48个,那么我们就可以通过哈希的方式将2520缩小到50,完美!!


AC代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 20;

int a[N], h[2520];
ll f[N][50][2520];

ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b % a, b);
}
ll lcm(ll a, ll b) {
	return a / gcd(a, b) * b;
}

ll dfs(int pos, int limit, int prelcm, int yu) {
	if (!pos) return yu % prelcm == 0;
	if (!limit && f[pos][h[prelcm]][yu] != -1)return f[pos][h[prelcm]][yu];
	ll res = 0;
	int end = limit ? a[pos] : 9;
	for (int i = 0; i <= end; ++i)
		res += dfs(pos - 1, limit && i == end, i == 0 ? prelcm : lcm(prelcm, i), (yu * 10 + i) % 2520);
	if (!limit)f[pos][h[prelcm]][yu] = res;
	return res;
}

ll dp(ll n) {
	int len = 0;
	while (n) {
		a[++len] = n % 10;
		n /= 10;
	}
	return dfs(len, 1, 1, 0);
}
int main(void)
{
	int cnt = 0;
	for (int i = 1; i <= 2520; ++i)
		if (2520 % i == 0) h[i] = ++cnt;
	int t; scanf("%d", &t);
	while (t--) {
		memset(f, -1, sizeof f);
		ll l, r; scanf("%lld %lld", &l, &r);
		printf("%lld\n", dp(r) - dp(l - 1));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值