Codeforces #55D (数位dp+离散化)

Description

Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.

Input

The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers li and ri (1 ≤ li ≤ ri ≤ 9 ·1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).

Output

Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).

Sample Input

Input
1
1 9
Output
9
Input
1
12 15
Output
2

让你找[l,r]区间中,能被自己各个非零数位整除的数的个数。一看就是满足区间减法。现在就讨论怎么求就行了。
首先lcm(1..9)=2520, int MOD=2520;保存每一个数是不现实的,所以我们就.保存x%MOD就行了。
preSum表示已经讨论的前面的几个数位的值(前串),preLcm表示前穿的Lcm。
这里注意到1...9的各种lcm可以离散化处理,只有48个,这样可以大大减少数组的空间。
我们再用flag来表示当前位的数字大小是否超过x对应位的大小
例:x=15666;当我们讨论到千位是1,2,3,4时,后面三位是随便选的,讨论千位是5是,百位就不能随便选了,要<=6,此时在千位我们就达到了边界。
剩下的交给dfs。
PS:有人把2520优化成252的,92ms过了...我1122ms...
代码如下:
 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 const int MAXN=25;
 6 const int MOD=2520;
 7 long long dp[MAXN][MOD][48];//dp[i][j][k]表示处理到第i位,前串数(取模后)是j,前串树lcm是k时,后面位随便变的合法情况的个数
 8 int index[MOD+10],bit[MAXN];//index表示1..9的各种组合lcm,bit是将数字的每一位拆开保存
 9 long long int gcd (long long int a,long long int b) {return (b==0)?a:gcd(b,a%b);}
10 long long int lcm (long long int a,long long int b){return a/gcd(a,b)*b;}
11 void init()//来找1...9之间各种组合的lcm
12 {
13     int num=0;
14     for (int i=1;i<=MOD;++i)
15     if (MOD%i==0)
16     index[i]=num++;
17 }
18 long long dfs (int pos,int preSum,int preLcm,bool flag)//pos当前位,flag前面几位是否达到边界
19 {
20     if (pos==-1)//讨论到最后一位
21     return preSum%preLcm==0;//如果这个数满足要求,+1
22     if (!flag && dp[pos][preSum][index[preLcm]]!=-1)//没达到边界而且访问过这个状态
23     return dp[pos][preSum][index[preLcm]];//直接return,记忆化搜索
24     long long ans=0;
25     int endd=flag?bit[pos]:9;//这位达到边界时,下一位从0到x的对应位变化。没达到边界是0...9变化
26     for (int i=0;i<=endd;i++)
27     {
28         int nowSum=(preSum*10+i)%MOD;//添加下一位数字,然后更新状态
29         int nowLcm=preLcm;
30         if (i)
31         nowLcm=lcm(nowLcm,i);
32         ans+=dfs(pos-1,nowSum,nowLcm,flag&&i==endd);
33     }
34     if (!flag)
35     dp[pos][preSum][index[preLcm]]=ans;
36     return ans;
37 }
38 long long calc (long long x)
39 {
40     memset(bit,0,sizeof bit);
41     int pos=0;
42     while (x)
43     {
44         bit[pos++]=x%10;
45         x/=10;
46     }
47     return dfs(pos-1,0,1,1);
48 }
49 int main()
50 {
51     int t;
52     long long int l,r;
53     init();
54     memset(dp,-1,sizeof dp);
55     scanf("%d",&t);
56     while (t--)
57     {
58         scanf("%I64d%I64d",&l,&r);
59         printf("%I64d
",calc(r)-calc(l-1));
60     }
61     return 0;
62 }
 
原文地址:https://www.cnblogs.com/agenthtb/p/5898404.html