codeforces 55D Beautiful numbers(数位dp)

来源:互联网 发布:淘宝发布宝贝规格分类 编辑:程序博客网 时间:2024/06/17 16:10

Beautiful numbers

题目链接:

http://codeforces.com/problemset/problem/55/D

解题思路:

题目大意:

求区间[x , y]中beautiful number的个数,a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.

算法思想:

一个数能被它的所有非零数位整除,则它一定能被它们的最小公倍数整除,而1到9的最小公倍数为2520,所以数位dp时我们只需保存前面那些位的最小公倍数就可进行状态转移即可,到边界时就把所有位的lcm求出了。为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只需记录它对2520的模即可,这样我们就可以设计出如下数位dp:dfs(pos,premod,prelcm,f),pos为当前位,premod为前面那些位对2520的模,prelcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限,这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。

AC代码:

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 20;const int MOD = 2520;ll dp[maxn][MOD][50];int digit[maxn],index[MOD+5];int GCD(int a,int b){    if(b == 0)        return a;    return GCD(b,a%b);}int LCM(int a,int b){    return a/GCD(a,b)*b;}ll dfs(int pos,int premod,int prelcm,bool edge){    if(pos == -1)        return (premod%prelcm) == 0;    if(!edge && dp[pos][premod][index[prelcm]] != -1)        return dp[pos][premod][index[prelcm]];    int tmp = edge?digit[pos]:9;    ll ans = 0;    for(int i = 0; i <= tmp; i++){        int nowlcm = prelcm;        int nowmod = (premod*10 + i)%MOD;        if(i)            nowlcm = LCM(prelcm, i);        ans += dfs(pos - 1, nowmod, nowlcm, edge && i == tmp);     }     if(!edge)            dp[pos][premod][index[prelcm]] = ans;     return ans;}ll cal(ll x){    int pos = 0;    while(x){        digit[pos++] = x%10;        x /= 10;    }    return dfs(pos-1,0,1,1);}void init(){    int cnt = 0;    memset(dp,-1,sizeof(dp));    for(int i = 1; i <= MOD; i++){        if(MOD%i == 0)            index[i] = cnt++;    }}int main(){    init();    int T;    scanf("%d",T);    while(T--){        ll l,r;        scanf("%I64d%I64d",&l,&r);        printf("%I64d\n",cal(r)-cal(l-1));    }    return 0;}


0 0