数位DP+离散化 Beautiful numbers

题目:

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)中能被它自己的各位数整除的数的个数;

解题思路:区间前缀和相减即可,因为l,r的范围太大,所以需要DP求解前缀和,这里用到了一个结论,能被所有位数都整除的数,一定能被它们的最小公倍数整除,1~9的最小公倍数是2520,所以在DP的时候对数模2520后,它依旧是1~9的倍数,这样就可以将数的范围缩小到2520,

我们设dp[pos][num][lcm]表示枚举到pos位,值为num(对2520取模后),最小公倍数为lcm,时满足题意的个数,num必须开到2520,如果lcm也开到2520,那么空间必然是要爆的,所以,我们需要hash,在dp数组中,只存放对应lcm值的下标.

下面是关于为什么可以对数取模2520后,计算它能不能被数位整除的证明:

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左右相等,证明成立

下面给出代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <string>
#include <math.h>
#include <sstream>
#include <stack>
#define ll long long
#define LL long long
const int MOD = 2520;
LL dp[20][2550][50];
//int dis[20];
int Hash[2550];
int a[20];
using namespace std;
ll gcd(ll a,ll b)
{
    if(b==0)
        return a;
    else
        return gcd(b,a%b);
}
void init()
{
    int k=0;
    for(int i=1;i<=MOD;i++)
    {
        if(MOD%i==0)
            Hash[i]=k++;
    }
}
ll dfs(int pos,int num,int lcm,bool limit)
{
    if(pos==-1)
        return num%lcm==0;
    if(limit==0&&dp[pos][num][Hash[lcm]]!=-1)
    {
        return dp[pos][num][Hash[lcm]];
    }
    int up=limit?a[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;i++)
    {
        ans+=dfs(pos-1,(num*10+i)%MOD,i?(lcm*i/gcd(lcm,i)):lcm,limit&&i==a[pos]);
    }
    if(limit==0)
        dp[pos][num][Hash[lcm]]=ans;
    return ans;
}
ll solve(ll x)
{
    int k=0;
    while(x)
    {
        a[k++]=x%10;
        x/=10;
    }
    return dfs(k-1,0,1,1);
}
int main()
{
    //cout<<~(-1)<<endl;
    int T;
    cin>>T;
    ll l,r;
    init();
    memset(dp,-1,sizeof(dp));
    while(T--)
    {
        cin>>l>>r;
        cout<<solve(r)-solve(l-1)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/80763033