• 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;
    }
  • 相关阅读:
    ServletContext笔记
    Session笔记
    Cookie笔记
    递归实现取数组最大值
    栈结构实现队列结构
    返回栈中最小元素的两种实现O(1)
    数组实现不超过固定大小的队列(环形数组)
    双向链表实现栈和队列
    Windows Server 2008 R2 / Windows Server 2012 R2 安装 .NET Core 3.1
    Windows 7 / Windows Server 2008 R2 升级至 SP1
  • 原文地址:https://www.cnblogs.com/algorithms/p/2668021.html
Copyright © 2020-2023  润新知