CF-55D-数位DP-Beautiful Number

本文介绍了一种计算给定区间内beautifulnumber数目的方法,其中beautifulnumber定义为能被其自身非零位整除的数字。通过分析beautifulnumber的性质,采用动态规划和离散化技术解决该问题。
摘要由CSDN通过智能技术生成

题意
求给定区间内 beautiful number 的数目
beautiful number :能被它自身非零位整除
思路
一个数字要被它的所有非零位整除,即被他们的LCM整除
若 x 为beautiful number
则有 x %LCM{DIGIT[xi]} == 0
⇒ x%MOD%LCM{DIGIT[xi]} == 0 其中 MOD为LCM{1…9}
我们可以记录DP[数位][num%MOD][num的LCM]
当且 num%MOD %num的LCM == 0 时 表示找到一个beautiful number
且 nextnum = num*10 +nextdigit
nextLCM = nextdigit ?LCM{num的LCM,nextdigit}:num的LCM
//nextdigit要不为零
这时我们可以拿三维数组存储结果 dp[20][2050][2050]
但是这样三维数组过大 无法运行
要将Num的LCM离散化
10以内最小公倍数组合 共有48种情况
故dp[20][2050][48]
这时就能存下了
接下来DFS解决

/*************************************************************************
    > File Name: main.cpp
    > Author:Chazz 
    > Created Time: 2015年11月04日 星期三 23时09分48秒
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <algorithm>
#include <vector>
#define MOD 2520
typedef long long ll;
using namespace std;
ll dp[21][MOD+5][55];
int inde[MOD+5];
int bit[20];
void init(){//初始化 且对NUM的LCM 离散化 
    int cnt = 0;
    memset(dp,-1,sizeof(dp));
    for(int i = 1;i <= MOD;i++){
        if(MOD%i==0){
            inde[i] = cnt++;
        }
    }
}
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
ll dfs(int pos ,int presum,int prelcm,bool flag){
    if(pos < 1){ //数位判断返回
        return ( presum%prelcm == 0);
    }

    if(!flag && dp[pos][presum][inde[prelcm]]!=-1){
        return dp[pos][presum][inde[prelcm]];
    }
    int end = flag?bit[pos]:9;
    ll ans = 0;
    for(int i = 0;i <= end;i++){
        ans += dfs(pos-1,(presum*10+i)%MOD,i==0?prelcm:lcm(prelcm,i),flag&&i==end);
    }
    if(!flag){
        dp[pos][presum][inde[prelcm]] = ans;
    }
    return ans;

}
ll solve(ll n){
    int len = 0;
    while(n){
        bit[++len] = n%10;
        n = n/10;
    }
    return dfs(len ,0,1,1);
}
int main(void){
    int t;
    init();
    ll a,b;
    cin>>t;
    while(t--){
        cin>>a>>b;
        cout<<solve(b)-solve(a-1)<<endl;
    }
    return 0 ;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值