CF 55D Beautiful numbers (数位DP入门)

题意是:求 [l,r] 区间内完美数的个数,完美数的定义:这个数能是所有位数的倍数,比如42,42是4的倍数,也是2的倍数。

考虑数位DP,

LL dfs(int pos,int now,int lc,int limit) 表示pos为,这个数为now,所有位数的LCM为lc,上限为limit。

那么边界的情况就是 now%lc ==0才返回1

注意数据的输入和输出要用long long 和I64d

还有一个问题,根据前面的DFS函数,记忆化DP为dp[20][2520][2520],2520为1-9的LCM,会MLE,但是在第三维的纪录LCM中不会出现2520种,只会出现大概几十种吧,这里我开到100就能过,那么每生成一个新的LCM,离散化,这里用的是use[]数组,写在DFS函数中,就不用单独的预处理了。

还有就是注意细节吧,我把数组开为dp[20][2520][100],错了好几次,2520访问不到,但是没有返回RE,返回了WA,喵了个咪的。

#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<iostream>
using namespace std;
#define INF 0x7f7f7f7f
#define LL long long
LL dp[20][2525][100];  //离散化后的数组
int use[2525];
int a[25],cnt;
int gcd(int a,int b)
{
    while(b!=0)
    {
        a=a%b;
        swap(a,b);
    }
    return a;
}
int lcm(int a,int b)
{
    return (a*b)/gcd(a,b);
}
LL dfs(int pos,int now,int lc,int limit)
{
    if(use[lc]==0)                      //便找边离散化
        use[lc]=cnt++;

    if(pos==-1)
    {
        if(now%lc==0)              //如果这个数%所有位数的lcm就返回1
            return 1;
        return 0;
    }
    if(!limit&&dp[pos][now][use[lc]]!=-1)
        return dp[pos][now][use[lc]];
    int up=limit ? a[pos]:9;
    LL ret=0;
    for(int i=0;i<=up;i++)
    {
        if(i!=0)
            ret+=dfs(pos-1,(now*10+i)%2520,lcm(lc,i),limit&&i==a[pos]);    //如果i==0,那么LCM(i,lc)为出问题
        else
            ret+=dfs(pos-1,(now*10+i)%2520,lc,limit&&i==a[pos]);
    }
    if(!limit)
        dp[pos][now][use[lc]]=ret;
    return ret;

}
LL solve(LL x)
{
    int pos=0;
    while(x)
    {
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,0,1,true);
}
int main()
{
    int t;
    cnt=0;
    memset(use,0,sizeof(use));
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--)
    {
        LL l,r;
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",solve(r)-solve(l-1));
    }
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值