杂题集萃[3]
题目描述
小N对于数字的大小一直都有两种看法。
第一种看法是,使用字典序的大小(也就是我们常用的判断数字大小的方法,
假如比较的数字长度不同,则在较短一个前面补齐前导0,再比较字典序),
比如\(43<355\),\(10<11\)。第二种看法是,对于一个数字\(a_ka_{k-1}a_{k-2}...a_0\),
定义他的权值为,也就\(a_k×a_{k-1}×a_{k-2}×...×a_0\) 是各个数位的乘积。
现在给定两个区间,[L,R]与[L1,R1]。小N现在想知道,有多少使用字典序
判大小法在[L,R]之间的数字,满足其第二种定义的权值也在[L1,R1]之间。
换句话说,对于一个数x,定义f(x)为x的各个数位的乘积。对于L<=x<=R,问有
多少x满足,L1<=f(x)<=R1。
输入描述:
第一行四个整数L,R,L1,R1。
输出描述:
一行一个整数,代表小N想知道的数的数量。
-
输入
34 10000 24 57
-
输出
777
数据范围:
20%: L,R <= 10000000
40%: L,R <= 3*10^7
60%: L,R,L1,R1 <= 10^9
另外有20%:L1,R1<=1000
100%: 0<=L,R,L1,R1 <= 10^18, L <= R, L1 <= R1
题解
考虑数位DP,注意判定前导零。
当然也可以用记忆化搜索233。
code
- 记忆化搜索
#include <bits/stdc++.h>
#define int long long
using namespace std;
int l,r,L,R;
map<int,int>f[30];
int lim[30];
inline int dfs(int p,int sum,bool jud){
// p 从高到低处理到第几位
// sum 数字的各位的乘积,-1为前导零
// jud 1为等于 0为小于
if(!p){
if(sum==-1) sum=0;
return (L<=sum&&sum<=R);
}
if(!jud&&f[p][sum]) return f[p][sum]-1;
int tmp=jud?lim[p]:9,ans=0;
for(int i=0;i<=tmp;++i)
if(sum==-1){
if(i==0) ans+=dfs(p-1,-1,jud&&(i==tmp));
else ans+=dfs(p-1,i,jud&&(i==tmp));
}
else ans+=dfs(p-1,i*sum,jud&&(i==tmp));
if(!jud)f[p][sum]=ans+1;
return ans;
}
inline int slove(int x){
if(x==-1) return 0;int p=0;
while(x) lim[++p]=x%10,x/=10;
return dfs(p,-1,1);
}
signed main(){
scanf("%lld %lld %lld %lld",&l,&r,&L,&R);
printf("%lld",slove(r)-slove(l-1));return 0;
}