CF55D Beautiful numbers (数位dp)

题目链接

题解

一个数能被一些数整除,那么一定被这些数的\(lcm\)整除

那么我们容易想到根据\(lcm\)设状态

我们可以发现有用的\(lcm\)只有\(48\)

那么按照一般的数位\(dp\)

设出状态:\(f_{i,j,k,0/1}\)表示前\(i\)位,\(lcm=j\),模\(lcm\)的余数是\(k\),是否达到上界

但是这样子是无法转移的(因为新添加一个数模数可能会产生变化)

那么我们把模数统一成\(2520\)

复杂度\(O(T*L*48*2500*2)\)

其中\(L\)是输入数的位数

然后就会\(TLE\)

考虑稍微优化一下。

因为是多组询问。

如果之前已经询问过一个很大的数了,

这一次询问其实有很多答案我们已经知道了。

我们可以去掉状态的\(01\)那一维。

这样复杂度就可以去掉那个询问组数了。

具体看第二个代码。

Code

int gcd(int x, int y) {
	return !y ? x : gcd(y, x % y);
}
int lcm(int x, int y) {
	if (!x || !y) return x | y;
	return x * y / gcd(x, y);
}
LL dfs(int x, int n, int m, int op) {
	if (!x) return !num[n] || m % num[n] == 0;
	if (f[x][n][m][op] != -1) return f[x][n][m][op];
	LL &res = f[x][n][m][op]; res = 0;
	for (int i = 0; i <= (op ? a[x] : 9); i++)
		res += dfs(x - 1, M[lcm(num[n], i)], (m * 10 + i) % 2520, op & i == a[x]);
	return res;
}
LL calc(LL x) {
	if (!x) return 1;
	l = 0;
	while (x) a[++l] = x % 10, x /= 10;
	for (int i = 1; i <= l; i++)
		memset(f[i], -1, sizeof(f[i]));
	return dfs(l, 0, 0, 1);
}
void solve() {
	LL l = gi<LL>(), r = gi<LL>();
	printf("%lld\n", calc(r) - calc(l - 1));
	return ;
}
int main() {
	for (int i = 1; i < (1 << 9); i++) {
		int d = 0;
		for (int j = 0; j < 9; j++)
			if (i >> j & 1)
				d = lcm(d, j + 1);
		if (!M[d]) num[++cnt] = d, M[d] = cnt;		
	}
	int T = gi<int>();
	while (T--) solve();
	return 0;
}

LL dfs(int x, int n, int m, int op) {
	if (!x) return !num[n] || m % num[n] == 0;
	if (!op && f[x][n][m] != -1) return f[x][n][m];
	LL res = 0;
	for (int i = 0; i <= (op ? a[x] : 9); i++)
		res += dfs(x - 1, M[lcm(num[n], i)], (m * 10 + i) % 2520, op & i == a[x]);
	if (!op) f[x][n][m] = res;
	return res;
}
posted @ 2019-10-21 21:58  zzy2005  阅读(146)  评论(0编辑  收藏  举报