杂题集萃[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;
}
posted @ 2018-09-16 14:54  Sparks_Pion  阅读(144)  评论(0编辑  收藏  举报