CodeForces - 55D(数位dp,离散化)

时间:2021-08-03 16:15:17

题目来源:http://codeforces.com/problemset/problem/55/D

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

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

Beautiful numbers定义:能被自身所有非零数位整除的数。就比如15是Beautiful numbers,因为15可以被1,5整除,14不是,因为14不能被4整除。

问:n到m区间内有多少个Beautiful numbers

解题思路:一眼看很容易想到这是一道数位dp的题,我们可以想到:如果这个数是 Beautiful numbers,那么它一定可以被他的所有非零数位的最小公倍数整除。

1-9的最小公倍数是2520

我们可以定义某个状态dp[i][j][k]为第i号数位,j为当前表示的数字,k为当前数位的最小公倍数

如果直接这样写的话,j的范围就是1 ~ 9 ·1018,这肯定是不行的,这里有一个取模的规律:a%(b*c)%c=a%c

所以我们就可以把j的范围缩小到1-2520(1到9的最小公倍数)了。

然而这样还是不行,因为无法开出20*2520*2520的数组,我们可以知道1-9的组合的最小公倍数一定可以被2520整除的,所以先把先对1-2520进行离散化,离散化后可以发现其实只有48个有用的而已,那么就可以开20*2520*50的数组来

存状态了。

最小公倍数:    1  2  3  4  5  6  7  8  9  10  12  14  15  18  20  ...  1260  2520

hash1的值:1  2  3  4  5  6  7  8  9  10  11  12  13  14  15   ...  47  48

比如我们查找1-20的beautiful numbers 有以下过程(以下说明过程使用的数组以代码中为标准):

首先进入solve函数得到:a【0】=0,a【1】=2。接下来进入dfs函数:最先我们会得到dp【-1】【0】【1】=1,dp【-1】【1】【1】=1,dp【-1】【2】【2】=1,dp【-1】【3】【3】=1。。。dp【-1】【9】【9】=1(这里的-1只是模拟过程,并不代表实际情况)

的值,然后因为此时limit=false,所以dp【0】【0】【1】=sum{这些值的和}=10

同理可以得到10-19的beautiful number dp【-1】【10】【1】=1,dp【-1】【11】【1】=1,dp【-1】【12】【2】=1.。。dp【-1】【19】【9】=0;

此时limit=false ,dp【0】【1】【1】=4;

最后20也为beautiful number所以sum=15,但是我们在计算0-9的时候,误把0也记录了,所以最终答案为14.

这里插上本人的AC代码。。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int maxn=+;
ll dp[][maxn][];
int a[],hash1[maxn];
inline ll gcd(ll i,ll j){
return j==?i:gcd(j,i%j);
}
inline ll lcm(ll i,ll j){
return i/gcd(i,j)*j;
}
ll dfs(ll pos,ll sum,ll sum1,bool limit){
if(pos==-){
return sum%sum1==;
}
if(!limit&&dp[pos][sum][hash1[sum1]]!=-){
// cout<<pos<<endl;
// cout<<dp[pos][sum][hash1[sum1]]<<endl;
return dp[pos][sum][hash1[sum1]];
}
ll up=limit?a[pos]:;
ll ans=;
for(int i=;i<=up;i++){
ans+=dfs(pos-,(sum*+i)%,i==?sum1:lcm(sum1,i),(i==up)&&limit);
}
if(!limit)
dp[pos][sum][hash1[sum1]]=ans;
return ans;
}
ll solve(ll s){
memset(a,,sizeof(a));
int len=;
while(s){
a[len++]=s%;
s/=;
}
return dfs(len-,,,true);
}
int main(){
int t;
int cnt=;
ll l,r;
scanf("%d",&t);
for(int i=;i<=;i++)
{
if(%i==)
hash1[i]=cnt++;
}
memset(dp,-,sizeof(dp));
while(t--){
cin>>l>>r;
//cout<<solve(l-1)<<" "<<solve(r)<<endl;
cout<<solve(r)-solve(l-)<<endl;
//cin>>l;
// cout<<solve(l)<<endl;
}
return ;
}