问题描述
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.
输入格式
The first line of the input contains the number of cases t ( 1<=t<=10 ). Each of the next tt lines contains two natural numbers (l_{i} and r_{i}( 1<=l_{i}<=r_{i}<=9·10^{18} )).
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 should contain tt numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri , inclusively).
输入格式
1
1 9
输出格式
9
题目翻译
题目描述
Volodya是一个很皮的男♂孩。他认为一个能被它自己的每一位数上的数整除的数是很妙的。我们先忽略他的想法的正确性,只回答在l到r之间有多少个很妙的数字。
输入输出格式
输入:总共有t个询问:
第一行:t;
接下来t行:每行两个数l和r。
注意:请勿使用%lld读写长整型(虽然我也不知道为什么),请优先使用cin(或者是%I64d)。
输出:t行,每行为一个询问的答案。
解析
首先,对于区间询问,我们可以变成1到r的值减去1到l-1的值。那么,决策阶段为当前到了第几位。考虑如何将状态描述清楚。这里需要几个性质:
- 如果一个数整除一些数,那么这个数一定整除这些数的倍数。
- 如果一个数整除一些数的一部分,那么这一部分数的最小公倍数一定是所有数的最小公倍数的因数。
由性质1,我们只需要记录每一位数的最小公倍数即可。而1到9的最小公倍数是2520,那么要判断当前数是否满足要求,可以将当前数模2520,在判断余数是否整除所有位的最小公倍数。因此,我们有如下状态设计:
设(f[i][j][k])表示当前还有i位数,组成的数模2520余j,所有位数的最小公倍数为k的满足要求的数的个数。那么,就可以用数位DP的板子解决这个问题了。状态转移方程如下:
注意处理边界的问题。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
using namespace std;
int t,l,r,i,f[20][2522][50],d[20],id[2522],cnt;
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
int lcm(int a,int b)
{
if(a==0||b==0) return a+b;
return a*b/gcd(a,b);
}
int dp(int i,int j,int k,bool flag)
{
if(i==0){
if(j%k==0) return 1;
return 0;
}
if(!flag&&f[i][j][id[k]]!=-1) return f[i][j][id[k]];
int lim=flag?d[i]:9,ans=0;
for(int x=0;x<=lim;x++){
if(x==lim) ans+=dp(i-1,(j*10+x)%2520,lcm(k,x),flag);
else ans+=dp(i-1,(j*10+x)%2520,lcm(k,x),0);
}
if(!flag) f[i][j][id[k]]=ans;
return ans;
}
int cal(int x)
{
int num=0;
while(x){
d[++num]=x%10;
x/=10;
}
return dp(num,0,1,1);
}
signed main()
{
memset(f,-1,sizeof(f));
for(i=1;i<=2520;i++){
if(2520%i==0) id[i]=++cnt;
}
t=read();
while(t--){
l=read();r=read();
cout<<cal(r)-cal(l-1)<<endl;
}
return 0;
}
反思
没有发现原题的要求等价于整除他们的最小公倍数,并且可以用模2520的值来代替原来构造的数。多多关注特殊条件特殊性质。