bzoj 3679 数字之积

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值