A - Beautiful numbers (数位dp)

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范围内能被本身每个非零位整除的数的个数。(1 ≤ l ≤ r ≤ 9 ·1e18) 
题目分析: 
第一个要知道的知识点: 
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
左右相等,证明成立  

因为我们数位dp是枚举的每一位,所以dfs枚举到当前位时要知道上一种状态的和,因为要算取模嘛。但是这个数据范围太大,当枚举9位之后的我们没办法开这么大的数组来记录。所以我们利用 sum%(x*n)%x == sum%x;这个来给它取模变小,这是n取2520是最合适的,以为它是1~9的最小公倍数。这就是为什么代码里面left要%2520。 
第二个知识点: 
就是怎样实现一个数能整除所有组成它的非0数字。 我们枚举到当前位的时候已经知道上一步的left了,我们用一个lcm来记录上个状态的最小公倍数,和这个状态的i求最小公倍数就是要传递下去的最小公倍数。但是这个最小公倍数最大能到是2520,我们不可能去开一个dp[19][2520][2520],所以要想办法变小。所以对lcm进行离散化,因为lcm一定可以整除2520,所以将1~2520可以整除2520的数进行标记即可,测试后发现只有48个,满足当前情况。
代码:


//Full of love and hope for life

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
//https://paste.ubuntu.com/
//https://www.cnblogs.com/zzrturist/    //博客园
//https://blog.csdn.net/qq_44134712     //csdn

using namespace std;
const int maxn=5e3+10;
typedef long long ll;
const int mod=2520;

int dight[20],has[2550];
ll dp[25][50][2550],n,m;

ll dfs(int len,int left,int lcm,bool limit){
    if(len==0){
        return left%lcm==0;
    }
    if(!limit&&~dp[len][has[lcm]][left]){
        return dp[len][has[lcm]][left];
    }
    ll ans=0,upbound=(limit?dight[len]:9);
    for(int i=0;i<=upbound;i++){
        ans+=dfs(len-1,(left*10+i)%mod,i?(lcm*i/__gcd(lcm,i)):lcm,i==dight[len]&&limit);
    }
    if(!limit){
        dp[len][has[lcm]][left]=ans;
    }
    return ans;
}

ll solve(ll num){
    int k=0;
    while(num){
        dight[++k]=num%10;
        num/=10;
    }
    return dfs(k,0,1,true);
}

int main(){
    int t;
    scanf("%d",&t);
    int cnt=0;
    for(int i=1;i<=mod;i++){
        if(mod%i==0){
            has[i]=cnt++;
        }
    }
    memset(dp,-1,sizeof(dp));
    while(t--){
        scanf("%lld%lld",&n,&m);
        printf("%lld\n",solve(m)-solve(n-1));
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZ --瑞 hopeACMer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值