CodeForces - 55D (数位dp + 离散化 + 数论)

思路

首先我们要知道,一个数字num能够被所有的位数(除了0)整除,说明num是它所有的位数的公倍数。记所有数位的最小公倍数为LCM,则有 n u m % L C M = = 0 num \% LCM == 0 num%LCM==0

容易知道,1、2、3……8、9的最小公倍数为2520。

2520的因数一共有48个,题目给的内存不够我们开dp[25][2530][2530],所以我们把最后一个维度(2520的因数)离散化编号。

然后进行数位dp即可

代码

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long ll;
const int MOD = 2520;
int Hash[2530];
int digit[25];
ll dp[25][2530][50];
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b){
    return a * b / gcd(a, b);
}
ll dfs(int pos, int mod, int Lcm, bool limit){
    if(pos == 0)
        return mod % Lcm == 0;
    if(!limit && dp[pos][mod][Hash[Lcm]] != -1)
        return dp[pos][mod][Hash[Lcm]];
    ll ans = 0;
    int up = limit ? digit[pos] : 9;
    for(int i = 0; i <= up; i++)
    {
        if(i == 0){
            ans += dfs(pos - 1, (mod * 10 + i) % MOD, Lcm, limit && i == up);
        }else{
            ans += dfs(pos - 1, (mod * 10 + i) % MOD, lcm(Lcm, i), limit && i == up);
        }
    }
    if(!limit)
        dp[pos][mod][Hash[Lcm]] = ans;
    return ans;
}
ll bit(ll num){
    int len = 0;
    while(num){
        digit[++len] = num % 10;
        num/= 10;
    }
    return dfs(len, 0, 1, 1);
}
void init(){
    int cnt = 0;
    int num = 2520;
    for(int i = 1; i <= num; i++)
        if(!(num % i))
            Hash[i] = ++cnt;
}
int main(){
    init();//哈希
    memset(dp, -1, sizeof(dp));

    int t;
    scanf("%d", &t);

    while(t--){
        ll l, r;
        scanf("%I64d %I64d", &l, &r);
        printf("%I64d\n", bit(r) - bit(l - 1));
    }
    return 0;
}

参考来源

博客
https://www.cnblogs.com/dilthey/p/8542011.html
博客
https://www.cnblogs.com/neopenx/p/4063647.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值