CF55D Beautiful numbers

题目链接

题意

定义一个数字(x)(beautiful number)当且仅当(x)可以被其十进制表示下所有非(0)位置的数整除。

例如(24)是一个(beautiful number),因为他可以被(2)(4)整除。

(28)不是一个(beautiful number),因为他不能被(8)整除

给出两个数字(L,R; (1 le Lle R le 10^{18}))

求出区间([L,R])内有多少(beautiful number)

思路

首先显然的数位(dp)

先不考虑空间和时间问题

要让一个数字(x)整除所有数位上的数字。其实也就是要整除这些数字的最小公倍数((lcm))

(f[i][j][k])表示当前到了第(i)位,当前数字为(j;) (先不管能否空间是否足够)。所选数字的(lcm)(k)的方案数。

搜到最后看一下(lcm)是否整除(j)即可。

然后考虑空间问题。

(f[i][j][k])中,(i)(18)左右,(j)(10^{18})(k)最大是(2520)((2520)(1) ~ (9)(lcm))

考虑优化一下(j)这一维。

显然所有可能的(lcm)都是(2520)的因数。

而比较显然的

[x \% p = x \% kp \% p ]

所以我们可以把(j)那一维的数以(2520)为模数(hash)一下。

然后优化(k)那一维。

枚举一下可以发现。(1)~(9)的所有可能组合中。(lcm)的种类其实只有(50)种左右。所以就可以把最后一维压成(50)左右

然后就可以愉快的数位(dp)啦!

代码

/*
* @Author: wxyww
* @Date:   2019-03-17 08:30:41
* @Last Modified time: 2019-03-17 19:36:46
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int LCM = 2520;
#define int ll
ll read() {
	ll x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
ll L, R;
int dy[100], pk[LCM + 100];
int get_lcm(int x, int y) {
	if (!y) return x;
	return x * y / __gcd(x, y);
}
namespace BF2 {
	int pos, f[20][3000][60], a[20];
	ll dp(ll p, ll now, ll lcm, int lim) {
		if (!p) return now % dy[lcm] == 0;
		if (f[p][now][lcm] != -1 && !lim) return f[p][now][lcm];
		int up = 9;
		if (lim) up = a[p];
		int tmp = 0;
		for (int i = 0; i <= up; ++i) 
			tmp += dp(p - 1, ((now * 10 + i) % LCM), pk[get_lcm(dy[lcm], i)], lim & i == up);
		if (!lim) f[p][now][lcm] = tmp;
		return tmp;
	}
	ll solve(ll x) {
		pos = 0;
		while (x) {
			a[++pos] = x % 10; x /= 10;
		}
		return dp(pos, 0, 1, 1);
	}
	void main() {
		cout << solve(R) - solve(L - 1) << endl;
	}
}
signed main() {
	int T = read();
	memset(BF2::f, -1, sizeof(BF2::f));
	int cnt = 0;
	for (int i = 1; i <= LCM; ++i) {
		if (LCM % i == 0) {
			dy[++cnt] = i; pk[i] = cnt;
		}
	}
	while (T--) {
		L = read(), R = read();
		BF2::main();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/CF55D.html