Codeforces Beta Round #51---D. Beautiful numbers(数位dp, 巧妙)

本文介绍了一种使用数位动态规划方法解决特定数学问题的技巧。该问题要求计算在给定范围内能被其所有非零位数整除的整数数量。通过将问题转化为求这些数对2520取模后的余数是否为0,利用数位DP进行高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.
Input

The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers li and ri (1 ≤ li ≤ ri ≤ 9 ·1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).
Output

Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).
Sample test(s)
Input

1
1 9

Output

9

Input

1
12 15

Output

2

这题做法真的很精妙
如果一个数可以被它每一位上的数整除,那一定可以被那些数的LCM整除,最大的LCM是2520,因此那些数一定能被2520的因子某个整除
设dp[cur][rest][lcm]表示到第cur位,前面的数组成的数对2520取模为rest,之前的数的lcm为lcm的数的个数,直接这样会超内存,所以第三维离散化一下

前面的数组成的数对2520取模后为什么对答案没有影响呢?
设这个数为2520 * k + b,对lcm取模,lcm是2520的一个因子,因此:
(2520 * k + b) % lcm = b % lcm
所以不影响

然后就是数位dp的事情了
显然要用64位整数

/*************************************************************************
    > File Name: cf55d.cpp
    > Author: ALex
    > Mail: zchao1995@gmail.com 
    > Created Time: 2015年02月23日 星期一 22时25分40秒
 ************************************************************************/

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const double eps = 1e-15;
typedef long long LL;
typedef pair <int, int> PLL;

LL dp[33][2600][60];
int LCM_ARR[60];
int HASH[3000];
int cnt;
int bit[30];

LL gcd (LL a, LL b)
{
    return b ? gcd (b, a % b) : a;
}

LL LCM (LL a, LL b)
{
    return a / gcd (a, b) * b;
}

LL dfs (int cur, int rest, int ind, bool flag, bool zero)
{
    if (cur == -1)
    {
        if (zero)
        {
            return 0;
        }
        return (rest % LCM_ARR[ind] == 0);
    }
    if (!flag && ~dp[cur][rest][ind])
    {
        return dp[cur][rest][ind];
    }
    LL ans = 0;
    int end = flag ? bit[cur] : 9;
    for (int i = 0; i <= end; ++i)
    {
        if (zero && !i)
        {
            ans += dfs (cur - 1, 0, 0, flag && (i == end), 1);
        }
        else if (zero && i)
        {
            ans += dfs (cur - 1, i, HASH[i], flag && (i == end), 0);
        }
        else
        {
            if (i == 0)
            {
                ans += dfs (cur - 1, rest * 10 % 2520, ind, flag && (i == end), zero && (i == 0));
                continue;
            }
            int lcm = LCM (LCM_ARR[ind], i);
            ans += dfs (cur - 1, (rest * 10 + i) % 2520, HASH[lcm], flag && (i == end), zero && (i == 0));
        }
    }
    if (!flag)
    {
        dp[cur][rest][ind] = ans;
    }
    return ans;
}

LL calc (LL n)
{
    int ret = 0;
    while (n)
    {
        bit[ret++] = n % 10;
        n /= 10;
    }
    return dfs (ret - 1, 0, 1, 1, 1);
}

int main ()
{
    cnt = 0;
    memset (dp, -1, sizeof(dp));
    for (int i = 1; i <= 2520; ++i)
    {
        if (2520 % i == 0)
        {
            LCM_ARR[++cnt] = i;
            HASH[i] = cnt;
        }
    }
    LL l, r;
    int t;
    scanf("%d", &t);
    while (t--)
    {
        cin >> l >> r;
        cout << calc (r) - calc (l - 1) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值