CF55D Beautiful numbers

题目描述

Volodya是一个很皮的男♂孩。他认为一个能被它自己的每一位数上的数整除的数是很妙的。我们先忽略他的想法的正确性(如需证明请百度“神奇海螺”),只回答在l到r之间有多少个很妙的数字。

输入输出格式

输入:总共有t个询问:

第一行:t;

接下来t行:每行两个数l和r。

注意:请勿使用%lld读写长整型(虽然我也不知道为什么),请优先使用cin(或者是%I64d)。

输出:t行,每行为一个询问的答案。

输入输出样例
输入 #1复制
1
1 9
输出 #1复制
9
输入 #2复制
1
12 15
输出 #2复制
2

这是一道数位DP题,大体上只要把数位DP记忆化搜索的板子改一改就行了
首先我们确定DP方程,他要求被可以被每一位的最小公倍数整除,因此我们需要开十以内的最小公倍数2520的内存来存储当前数除以2520的余数,另外还要存储从头开始的pos位置。此外,我们还需要存储到目前pos位置处的最小公倍数是多少,由于这里的最小公倍数并不连续,而是离散的,因此我们需要对其使用离散化处理,记住map不能用,会超时…
然后套一个数位DP的模板即可

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N = 22;
const int M = 55;

int T,n,m,a[N],f[N][2522][M];
vector<int> alls;
int hsh[2522];

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

int llcm(int a,int b){
    return a/gcd(a,b)*b;
}

void init(){
    memset(f,-1,sizeof f);
    for(int i=1,j=0;i<=2520;i++)
    	if(2520%i==0) hsh[i]=j++;
}

int dp(int pos,int mod,int lcm,bool flag){
    if(pos==0) return mod%lcm==0;
    if(flag&&f[pos][mod][hsh[lcm]]!=-1) return f[pos][mod][hsh[lcm]];
    int x= flag?9:a[pos];
    int ans=0;
    for(int i=0;i<=x;i++){
        int tlcm = i?llcm(lcm,i):lcm;
        int tmod = (mod*10+i)%2520;
        ans+=dp(pos-1,tmod,tlcm,flag||i<x);
    }
    if(flag) f[pos][mod][hsh[lcm]]=ans;
    return ans;
}

int calc(int x){
    memset(a,0,sizeof a);
    int pos=0;
    while(x){
        a[++pos]=x%10;
        x/=10;
    }
    return dp(pos,0,1,0);
}

signed main(){
    init();
    cin >> T;
    while(T--){
        cin >> n >> m;
        cout << calc(m)-calc(n-1) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值