codeforces55D_数位dp

题目链接

参考链接:http://blog.csdn.net/to_be_better/article/details/50658392

题意:

求一个区间内的Beautiful numbers有多少个。Beautiful numbers指:一个数能整除所有组成它的非0数字。 
例如15可以被1和5整除,所以15是Beautiful numbers。

思路

Beautiful numbers指:一个数能整除所有组成它的非0数字。 
等同于 一个数能整除 所有组成它的非0数字的最小公倍数。 
我们看到数据的范围是1 ≤ li ≤ ri ≤ 9 ·1018,根本无法记录。 
所以,缩小范围成为第一要做的事。 
先看一个小式子。

    sum%(x*n)%x == sum%x;

证明:设sum = k*x+b
    等号左边:
        sum%(x*n)%x -> (k*x+b)%(x*n)%x 
        将k转为ka*n + kb代入;
        (ka*n*x+kb*x+b)%(x*n)%x -> (kb*x+b)%x -> b%x -> b
    等号右边:
        b
左右相等,证明成立
那么我们就可以用上式中的x*n对num进行取余,记录其取余后的值,显然,1~9的最小公倍数2520是最合理的x*n。 
而在逐位统计时,可以直接由前面位取余后的值来得到包含新一位的新数字取余后的值。 
例如 RX(R是已知前面位取余后的值),那么Rx%2520 == (R*10+x)%2520。就不在此废话证了。 
我们使用记忆化搜索。 
**dfs(len, num, lcm, flag) 
len表示迭代的长度, 
num为截止当前位的数对2520取余后的值。 
lcm为截止当前位的所有数的最小公倍数。 
flag表示当前数是否可以任意取值(对取值上限进行判断)** 
则可用dp[len][num][lcm]来对其进行记录。 
但lcm按2520取值根本开不下,所以对lcm进行离散化,因为lcm一定可以整除2520,所以将1~2520可以整除2520的数进行标记即可,测试后发现只有48个,满足当前情况。
 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <cstdio>
 6 #include <vector>
 7 #include <ctime>
 8 #include <queue>
 9 #include <list>
10 #include <set>
11 #include <map>
12 using namespace std;
13 #define INF 0x3f3f3f3f
14 typedef long long LL;
15 
16 LL dp[20][50][2550];
17 int fact[2550], bit[20];
18 void find()
19 {
20     memset(fact, 0, sizeof(fact));
21     int cnt = 0;
22     for(int i = 1;  i <= 2520; i++)
23         if(2520 % i == 0)
24             fact[i] = cnt++;
25 }
26 LL dfs(int len, int lcm, int mod, int flag)
27 {
28     LL sum = 0;
29     if(len == 0)
30         return mod % lcm == 0;
31     if(flag && dp[len][fact[lcm]][mod] >= 0)
32         return dp[len][fact[lcm]][mod];    
33     int te = flag ? 9 : bit[len];
34     for(int i = 0; i <= te; i++)
35     {
36         int Lcm = lcm;
37         if(i != 0){
38             int x = __gcd(lcm, i);
39             Lcm = lcm * i / x;
40         }
41         int Mod = (i + mod * 10) % 2520;
42         sum += dfs(len - 1, Lcm, Mod, flag || i < te);
43     }
44     if(flag)
45         dp[len][fact[lcm]][mod] = sum;
46     return sum;
47 }
48 LL solve(LL n)
49 {
50     int len = 0;
51     while(n)
52     {
53         bit[++len] = n % 10;
54         n /= 10;
55     }
56     return dfs(len, 1, 0, 0);
57 }
58 int main()
59 {
60     int t;
61     find();
62     LL l, r;
63     scanf("%d", &t);
64     memset(dp, -1, sizeof(dp));
65     while(t--)
66     {
67         scanf("%I64d %I64d", &l ,&r);
68         printf("%I64d\n", solve(r) - solve(l - 1));
69     }
70     return 0;
71 }
View Code

 

 
posted @ 2016-08-16 20:12  海无泪  阅读(153)  评论(0编辑  收藏  举报