http://www.elijahqi.win/archives/3694
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
Source
搜索都写挂..
考虑仅用1~9凑出的1e9内的数字最多只有5194个
于是我们把这个搜索一下 搜索出5194种情况 用map储存
搜索的时候不能规定位数 即使仅有18位 但因为我这种特殊写法 并不能规定搜索的位数
然后设dp[i][j]表示 位数为i 凑出第j种数字的方案数
预处理之后 用基础数位dp分别算出1~r-1 1~l-1的方案数 简单容斥
#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
map<int,int> mm;
const int N=5200;
int a[N],n,cnt;
ll dp[20][N],L,R;
inline void dfs(int x,int s){
if (s>n||mm[s]) return;
if (x) a[++cnt]=s,mm[s]=cnt;
for (int i=1;i<=9;++i)if ((ll)s*i<=n) dfs(x+1,s*i);
}
inline ll gao(ll m){
if(!m) return 0;
int l=0;ll now=1,p=1;ll ans=0;
while(p<=m/10) ++l,p*=10;
for (int i=1;i<=l;++i)
for (int j=1;j<=cnt;++j) ans+=dp[i][j];
for (;~l;--l,p/=10){
for (int i=1;i<m/p;++i){
for (int j=1;j<=cnt;++j) if (a[j]*i*now<=n) ans+=dp[l][j];
}now*=m/p;m%=p;if (now>n) return ans;if (!now) return ans;
}return ans+(now<=n);
}
int main(){
freopen("bzoj3679.in","r",stdin);
//freopen("bzoj3679.out","w",stdout);
scanf("%d%lld%lld",&n,&L,&R);
dfs(0,1);dp[0][1]=1;
//for (auto v=mm.begin();v!=mm.end();++v) printf("%d\n",v->first);
for (int i=0;i<19;++i){
for (int j=1;j<=cnt;++j){
if (!dp[i][j]) continue;int tmp=a[j];
for (int z=1;z<=9;++z){
if (tmp>n/z) continue;
dp[i+1][mm[tmp*z]]+=dp[i][j];
}
}
}printf("%lld\n",gao(R-1)-gao(L-1));
return 0;
}