Codeforces 55D. Beautiful numbers (数位DP)

题意:求区间[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,mod,lcm,f),pos为当前位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限,这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。

View Code
#include <stdio.h>
#include <string.h>
#define N 19
#define MOD 2520
typedef __int64 LL;
int t[200],cnt;
LL dp[N][MOD][48];
int digit[N];
int GCD(int a,int b)
{
    while(a%b)
    {
        int tmp=b;
        b=a%b;
        a=tmp;
    }
    return b;
}
int LCM(int a,int b)
{
    return a/GCD(a,b)*b;
}
int bs(int x)
{
    int mid,min=0,max=cnt;
    while(min+1!=max)
    {
        mid=min+max>>1;
        if(t[mid]>x)    max=mid;
        else    min=mid;
    }
    return min;
}
LL dfs(int pos,int mod,int lcmid,int f)
{
    if(pos==-1) return (mod%t[lcmid])?0:1;
    if(!f&&dp[pos][mod][lcmid]!=-1)   return dp[pos][mod][lcmid];
    int max=f?digit[pos]:9;
    LL ret=0;
    for(int i=0;i<=max;i++)
    {
        int nmod=(mod*10+i)%MOD;
        int nlcmid=lcmid;
        if(i)   nlcmid=bs(LCM(t[lcmid],i));
        ret+=dfs(pos-1,nmod,nlcmid,f&&i==max);
    }
    if(!f)  dp[pos][mod][lcmid]=ret;
    return ret;
}
LL cal(LL x)
{
    int pos=0;
    while(x)
    {
        digit[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,0,0,1);
}
void init()
{
    cnt=0;
    for(int i=1;i<=MOD;i++)
    {
        if(MOD%i==0)    t[cnt++]=i;
    }
    memset(dp,-1,sizeof(dp));
}
int main()
{
    int t;
    init();
    scanf("%d",&t);
    while(t--)
    {
        LL x,y;
        scanf("%I64d%I64d",&x,&y);
        printf("%I64d\n",cal(y)-cal(x-1));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/algorithms/p/2668021.html