【BZOJ3679】数字之积

Description

一个数x各个数位上的数之积记为f(x) <不含前导零>
求[L,R)中满足0<f(x)<=n的数的个数

Input

第一行一个数n
第二行两个数L、R

Output

一个数,即满足条件的数的个数

Sample Input

5
19 22

Sample Output

1

HINT

100%     0<L<R<10^18 , n<=10^9

题解:真心喜欢这种搜索+DP的题~

先预处理出f(x)所有可能的取值,然后设dp[i][j]表示有i位,f值为j的数的个数。

但是f(x)的值可能很多,不过f(x)的质因子只有2,3,5,7,所以DFS即可,最后合法的f值不会超过6000个。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map> using namespace std;
typedef long long ll;
map<ll,int> mp;
ll n,l,r;
int m;
ll now;
ll pri[]={2,3,5,7},val[6000],f[20][12][6000];
ll v[20];
void dfs(int dep)
{
if(now>n) return ;
if(dep==4)
{
mp[now]=++m,val[m]=now;
return ;
}
int t=0;
dfs(dep+1);
while(now*pri[dep]<=n) now*=pri[dep],t++,dfs(dep+1);
while(t--) now/=pri[dep];
}
ll calc(ll x)
{
if(!x) return 0;
ll ret=0;
int i,j,k,mx=0;
ll tmp=1;
while(x) v[++mx]=x%10,x/=10;
for(i=1;i<mx;i++) for(j=1;j<=9;j++) for(k=1;k<=m;k++) ret+=f[i][j][k];
for(i=mx;i;i--)
{
for(j=1;j<v[i];j++)
for(k=1;k<=m;k++) if(tmp*val[k]<=n) ret+=f[i][j][k];
tmp*=v[i];
if(!tmp||tmp>n) break;
}
if(!i) ret++;
return ret;
}
int main()
{
scanf("%lld%lld%lld",&n,&l,&r);
int i,j,k,h;
now=1,dfs(0);
for(i=1;i<=9;i++) f[1][i][mp[i]]=1;
for(i=2;i<=18;i++)
for(k=1;k<=9;k++)
for(j=1;j<=m;j++) if(f[i-1][k][j])
for(h=1;h<=9;h++) if(val[j]*h<=n)
f[i][h][mp[val[j]*h]]+=f[i-1][k][j];
printf("%lld",calc(r-1)-calc(l-1));
return 0;
}//1000 1 100
05-11 23:02