Codeforces 55D - Beautiful numbers(数位DP+离散化)

题目链接:http://codeforces.com/problemset/problem/55/D

题目大意:然你找出范围在[l,r]内,有多少个数满足可以被自身各个位上的数整除(0除外)。

解题思路:最开始给出这样一个记录状态的方式,dp[pos][mod][lcm],即对应位置,当上一位时的数本身的值,到上一位为止各个位的最小公倍数。因为1~9的最大公约数为2520,所以pos[1,20],mod[1,10^18],lcm[1,2520],这样数组所需空间太大了。于是对mod做了处理,令mod=mod%2520,因为lcm最大为2520,所以这样不会影响最终的mod%lcm的结果。

    但这样还是不够,我们发现2520的因子只有48个(加上1和它本身),相信大家都知道一个数的因子的因子,也是这个数的因子,一直往下推....我们就知道最顶上的数的因子包含了它所有的因子的因子。那么我们就可以得出结论,如果当某一位lcm不是2520的因子的时候那它最后无论怎么跟别的数取公倍数都不会是2520的因子,所以大可以把这中lcm都归为一类进行记录。所以我们可以进行离散化,再开一个HASH[lcm]数组,基于48个因子对应的序号,剩下都归为一类当成0。

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 typedef long long ll;
 5 const int N = 25;
 6 const int LCM = 2520 + 5;//1~9的最小公倍数是2520 
 7 ll a[N];
 8 ll dp[N][LCM][50];
 9 ll HASH[LCM];//离散化
10 
11 ll gcd(ll a, ll b) {
12     return b == 0 ? a : gcd(b, a%b);
13 }
14 
15 ll dfs(ll pos, ll mod, ll lcm, bool limit) {
16     if (pos == 0)  return mod%lcm == 0;
17     if (!limit&&dp[pos][mod][HASH[lcm]] != -1)    return dp[pos][mod][HASH[lcm]];
18     ll ans = 0;
19     ll up = limit ? a[pos] : 9;
20     for (int i = 0; i <= up; i++) {
21         ans += dfs(pos - 1, (mod * 10 + i) % 2520, i==0?lcm:lcm/ gcd(lcm, i)*i, limit && (i == up));
22     }
23     if (!limit)    dp[pos][mod][HASH[lcm]] = ans;
24     return ans;
25 }
26 
27 ll solve(ll n) {
28     ll top = 0;
29     while (n) {
30         a[++top] = n % 10;
31         n /= 10;
32     }
33     return dfs(top, 0, 1, 1);
34 }
35 
36 int main() {
37     ios::sync_with_stdio(false);
38     memset(dp, -1, sizeof(dp));
39     int num = 1;
40     for (int i = 1; i<= 2520; i++){
41         if (2520 % i == 0){
42             HASH[i] = num++;
43         }
44     }
45     ll t;
46     cin >> t;
47     while (t--) {
48         ll l, r;
49         cin >> l >> r;
50         ll ans = 0;
51         ans += solve(r) - solve(l - 1);
52         cout << ans << endl;
53     }
54 }

 

posted @ 2017-06-03 17:09  Yeader  阅读(219)  评论(2编辑  收藏  举报