数位dp-CodeForces - 55D-数%每位==0的个数,数位dp余数模板

博客围绕计算L到R区间内Beautiful numbers(能被自身所有非零数位整除的数)的数量展开。指出整除处理较难,通过取MOD(LCM{1,2,9}=2520)缩小范围,得出只需存x % MOD,还可对LCM{}离散化,用dp[20][3000][50]保存状态。

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

题目链接:http://codeforces.com/problemset/problem/55/D
题目大意:Beautiful numbers定义:能被自身所有非零数位整除的数。就比如15是Beautiful numbers,因为15可以被1,5整除,14不是,因为14不能被4整除。

问:L到R区间内有多少个Beautiful numbers在这里插入图片描述
思路:
这个题就是整除比较难处理。

1: int MOD = LCM{1,2,9} = 5 * 7 * 8 * 9 = 2520
2: 按照定义,数字x为Beautiful : x % LCM{digit[xi]} = 0 即 x % MOD % LCM{digit[xi]} = 0,所以可以只需存x % MOD,范围缩小了。

那么统计已经出现的LCM{}和MOD就行了。
由2可以推出结论:n%a = (n%k * a)%a (k!=0)
a = LCM {}, 因为所有的LCM{}都是2520的因数。所以:2520=k * LCM{}。

我们要确定:n%LCM{}==0, 只要确定(n%2520)%LCM{}==0就可以了。

那么状态就很好表示了,只有保存n%2520就可以了。

这个前面不要13的倍数那道题用过。

当然LCM{}可以离散化一下,只有48个。

dp[20][3000][50]就可以保存所有的状态了。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int mod=2520;

int a[20];
int index[3000];//1...9的各种lcm可以离散化处理,只有48个
LL dp[20][3000][50];
void LCMCSL()
{
    int cut=0;
    for(int i=1;i<=mod;i++)
    {
        if(mod%i==0)
        {
            index[i]=cut++;
        }
    }
}

LL LCM(LL a, LL b)
{
    if(a==0)
    {
        return b;
    }
    return a/__gcd(a, b)*b;
}

LL dfs(int pos, int mod, int Lcm, int mt)
{
    if(pos==-1)
    {
        return mod%Lcm==0;
    }

    if(!mt&&dp[pos][mod][index[Lcm]]!=-1)
    {
        return dp[pos][mod][index[Lcm]];
    }

    int Len=mt?a[pos]:9;
    LL ans=0;
    for(int i=0;i<=Len;i++)
    {
        ans+=dfs(pos-1, (mod*10+i)%2520, LCM(i, Lcm), i==a[pos]&&mt);
    }

    if(!mt)
    {
        dp[pos][mod][index[Lcm]]=ans;
    }

    return ans;

}

LL slove(LL x)
{
    int pos=0;
    while(x)
    {
        a[pos++]=x%10;
        x/=10;
    }

    return dfs(pos-1, 0, 1, 1);
}

int main()
{
    LCMCSL();
    int T;
    memset(dp, -1, sizeof(dp));
    scanf("%d",&T);
    while(T--)
    {
        LL L, R;
        scanf("%lld%lld",&L,&R);

        printf("%lld\n", slove(R)-slove(L-1));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值