题目描述
小N对于数字的大小一直都有两种看法。第一种看法是,使用字典序的大小(也就是我们常用的判断数字大小的方法,假如比较的数字长度不同,则在较短一个前面补齐前导0,再比较字典序),比如43<355,10<11。第二种看法是,对于一个数字,定义他的权值为,也就是各个数位的乘积。 现在给定两个区间,[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想知道的数的数量。
示例1
输入
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
心得:
这题折腾死我了写了15min,debug了20min,具体哪错了就看代码吧
代码
#include<bits/stdc++.h>
using namespace std;
template<typename A>
void read(A&a)
{
a=0;
char c=0;
int f=1;
while(c<'0'||c>'9')
(c=getchar())=='-'?f=-1:0;
while(c>='0'&&c<='9')
a=(a<<3)+(a<<1)+c-'0',c=getchar();
f==-1?a=-a:0;
}
char buf[30];
template<typename A>
void write(A a)
{
if(a<0)
putchar('-'),a=-a;
int top=0;
if(!a)
buf[top=1]='0';
while(a)
buf[++top]=a%10+'0',a/=10;
while(top)
putchar(buf[top--]);
}
long long L,R,L1,R1;int s[20];
map<long long,long long>dp[20];
long long f[20],t[20];
long long dfs(int x,long long y,int lim)
{
if(!x)
{
if(y)
return 1;
else
return 0;
}//if(y==0)return 0; 好像不能只要写,因为即使是0,也可以有数字。
if(!lim&&dp[x].count(y))
return dp[x][y];
long long ans=(lim?(s[x]?t[x-1]:f[x-1]+1):t[x-1]);
for(int i=1;i<=(lim?s[x]:9);++i)
ans+=dfs(x-1,y/i,lim&&(i==s[x]));
return !lim?dp[x][y]=ans:ans;//错误笔记:如果有lim就不能存进数组了
}
long long Solve(long long x,long long y){//错误笔记:注意这些参数的类型也要开long long啊,不能顺手打int
if(x<0||y<0)
return 0;//错误笔记:<0可能要特判一下,如果按照一般的程序处理会有问题
if(x==0)
return 1;//错误笔记:等于0可能也要特判,因为按照一般的程序,会返回0 ,实际上应该是1
int len=0;
long long p=x,ans=0;//错误笔记:包括P的类型也要开long long
while(p)
s[++len]=p%10,p/=10;
for(int i=1;i<=len;++i)
f[i]=f[i-1]+s[i]*t[i-1];
for(int i=len;i;--i)
{
int lim=(i==len?s[i]:9);
for(int j=1;j<=lim;++j)
ans+=dfs(i-1,y/j,i==len&&j==lim);
}
return ans;
}
int main()
{
read(L),read(R),read(L1),read(R1);
t[0]=1;
for(int i=1;i<=19;++i)
t[i]=t[i-1]*10;
write(Solve(R,R1)-Solve(L-1,R1)-Solve(R,L1-1)+Solve(L-1,L1-1));
putchar('\n');
}
来源:nkw