zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    poj1015
    poj1101
    poj1081
    poj1020
    深入浅出MFC文档视图架构之实例剖析 Love3的日志 网易博客
    简单问题:LPARAM 是一个long(32bit),双字,怎么分别得到高位字和低位字的值。
    VC MFC SDI/MDI框架各部分指针获取方式
    msado15.tlh(228) : error C2011: “LockTypeEnum”: “enum”类型重定义
    C#数据库方面好书
    excel2007密码破解
  • 原文地址:https://www.cnblogs.com/wxyww/p/CF55D.html
Copyright © 2011-2022 走看看