• 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;
    }
    
  • 相关阅读:
    JS LeetCode 1423. 可获得的最大点数简单题解
    SpringBoot 学集 (第六章) Docker
    Linux 学记 (第三章)
    Linux 学记 (第二章)
    Linux 学记 (第一章)
    SpringBoot 学集 (第五章) Web开发续
    SpringBoot 学集 (第四章)Web开发
    SpringBoot 学集 (第三章) 日志框架
    SpringBoot 学集 (第二章) 配置文件
    SpringBoot 学集 (第一章)
  • 原文地址:https://www.cnblogs.com/wxyww/p/CF55D.html
Copyright © 2020-2023  润新知