题目链接: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 }