CodeForces - 55D Beautiful numbers (数位DP)

时间:2022-12-16 11:59:40

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).

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

题意:求[l,r]中,满足能够整除自身各个数位的数的个数有多少
分析:要在这么大的范围中,求出数的个数,我们能够想到用数位DP,但是使用数位DP,需要找到当扫描到数的第num位的时候,能够满足的某些共性,得到相同的值。这样作为DP基础。
题目要求的是能够整除各个数位,但是我们不可能将每个数位用DP维度表现出现。通过数论知识,我们可以知道,如果一个数,整除一些数,那么它一定能整除这些的数的最小公倍数,
所以整除最小公倍数是一个充要条件。这个时候,最后判断是用得到的数,看是否能除尽所有数位的最小公倍数,所以当我们扫描到第num位的时候,需要知道是前面已经扫描过的所有数位的最小公倍数是多少,以及这些数表示的值是多少,由于这些数表示的值
会很大,所以我们需要取模,将它与所有数位的公倍数取模是不影响的,然后2520是1,2,3,4,5,6,7,8,9的公倍数,所以模上2520即可。
另外还要注意的是,因为内存的限制,在开DP维度的时候,必要要用到的是2520*20,所以用来储存所有数位的最小公倍数的位置,不能开到2520,因为是最小公倍数,实际上1-2520的很多位置是不需要用到的
所以可以直接将这些数离散化处理,再进行DP
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
vector<int>V;
int get_id(int x)
{
    return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
}
LL a[50];
LL dp[20][50][2550];
LL dfs(int num,LL id,bool limit,int now)
{
    if(num==-1)
    {
      if(now%V[id-1]==0)
      return 1;
      else
      return 0;
    }
    if(!limit&&dp[num][id][now]!=-1)
    return dp[num][id][now];

    int up=limit?a[num]:9;
    LL ans=0;
    for(LL i=0;i<=up;i++)
    {
      LL lcm=V[id-1];
      if(i==0)
      ans+=dfs(num-1,get_id(lcm),limit&&i==up,(now*10+i)%2520);
      else
      ans+=dfs(num-1,get_id(lcm*i/__gcd(lcm,i)),limit&&i==up,(now*10+i)%2520);
    }

   if(!limit)
   dp[num][id][now]=ans;
   return ans;
}

void init_dfs(LL pos,LL now)
{
    V.push_back(now);
    if(pos==10)
    return;
    for(LL i=pos;i<=9;i++)
    init_dfs(i+1,now*i/__gcd(i,now));
}
LL solve(LL x)
{
  int cnt=0;
  while(x>0)
  {
     a[cnt]=x%10;
     cnt++;
     x/=10;
  }
  return dfs(cnt-1,get_id(1),1,0);
}
LL t,l,r;
int main()
{
    init_dfs(2,1);
    sort(V.begin(),V.end());
    V.erase(unique(V.begin(),V.end()),V.end());
    scanf("%lld",&t);
    memset(dp,-1,sizeof(dp));
    while(t--)
    {
      scanf("%lld%lld",&l,&r);
      printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}