zoukankan      html  css  js  c++  java
  • CodeForces 55D Beautiful numbers



    D. Beautiful numbers
    time limit per test
    4 seconds
    memory limit per test
    256 megabytes
    input
    standard input
    output
    standard output

    Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.

    Input

    The first line of the input contains the number of cases t (1≤t≤10). Each of the next t lines contains two natural numbers li and ri (1≤liri≤9·1018).

    Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).

    Output

    Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).

    Sample test(s)
    input
    1
    1 9
    output
    9
    input
    1
    12 15
    output
    2
     

    1. /* 
    2.     a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. 
    3.     问一个区间内[l,r]有多少个Beautiful数字 
    4.     范围9*10^18 
    5.      
    6.     数位统计问题,构造状态也挺难的,我想不出,我的思维局限在用递推去初始化状态,而这里的状态定义也比较难 
    7.     跟pre的具体数字有关 
    8.  
    9.     问了NotOnlySuccess的,豁然开朗  Orz 
    10.      
    11.     一个数字要被它的所有非零位整除,即被他们的LCM整除,可以存已有数字的Mask,但更好的方法是存它们的LCM{digit} 
    12.     int MOD = LCM{1,2,9} = 5 * 7 * 8 * 9 = 2520 
    13.     按照定义,数字x为Beautiful :  
    14.     x % LCM{digit[xi]} = 0 
    15.     即 x % MOD % LCM{digit[xi]} = 0 
    16.     所以可以只需存x % MOD,范围缩小了 
    17.     而在逐位统计时,假设到了pre***(pre指前面的一段已知的数字,而*是任意变) 
    18.         ( preSum * 10^pos + next )  % MOD % LCM(preLcm , nextLcm) 
    19.     =  ( preSum * 10 ^ pos % MOD + next % MOD ) % LCM(preLcm , nextLcm) 
    20.     == 0 
    21.     而next,nextLcm是变量,上面的比较式的意义就是 
    22.     在已知pos , preSum , preLcm情况下有多少种(next,nextLcm)满足式子为0 
    23.     而这个就是一个重复子问题所在的地方了,需要记录下来,用记忆化搜索 
    24.     dfs(pos , preSum , preLcm , doing) 
    25.     加一个标记为doing表示目前是在计算给定数字的上限,还是没有上限,即***类型的 
    26.     这样就将初始化以及逐位统计写在一个dfs了,好神奇!!! 
    27.      
    28.     还有一点,10以内的数字情况为2^3 , 3^2 , 5 , 7 
    29.     所以最小公倍数组合的情况只有4*3*2*2 = 48 
    30.     可以存起来,我看NotOnlySuccess的写法是 
    31.     for(int i = 1 ; i <= MOD ; i ++) 
    32.     { 
    33.         if(MOD % i == 0) 
    34.             index = num++; 
    35.     } 
    36.     很棒!! 
    37.  
    38.     所以复杂度大概为19*2520*48*10(状态数*决策数) 
    39.  
    40.     我觉得这题状态的设计不能跟具体数字分开,否则会很难设计吧 
    41.     所以用记忆化搜索,存起来 
    42.     用具体数字去计算,重复的子问题跟pre关系比较密切 
    43.     有一个比较重要的切入点就是LCM,还有%MOD缩小范围,才能存储 
    44.  
    45.     还有优化到只需%252的,更快 
    46.     不过我觉得%2520比较好理解 
    47. */  


    #include <iostream>
    #include <cstdio>
    #include <cstring>

    using namespace std;

    typedef long long int LL;

    const int MOD=2520;

    LL Gcd(LL a,LL b)
    {
        return (a==0)?b:Gcd(b%a,a);
    }

    LL Lcm(LL a,LL b)
    {
        if(a>b) swap(a,b);
        return a/Gcd(a,b)*b;
    }

    LL dp[20][MOD+10][50],lcm[MOD+10],bit[20];

    void Init()
    {
        int num=0;
        for(int i=1;i<=MOD;i++)
        {
            if(MOD%i==0)
                lcm=num++;
        }
        memset(dp,-1,sizeof(dp));
    }

    LL dfs(int pos,int presum,int preLcm,bool limit)
    {
        if(pos==-1)
            return presum%preLcm==0;
        if(!limit&&~dp[pos][presum][lcm[preLcm]]) return dp[pos][presum][lcm[preLcm]];
        int end=limit?bit[pos]:9;
        LL ans=0;
        for(int i=0;i<=end;i++)
        {
            int newsum=(presum*10+i)%MOD;
            int newLcm=preLcm;
            if(i)
            {
                newLcm=Lcm(preLcm,i);
            }
            ans+=dfs(pos-1,newsum,newLcm,limit&&i==end);
        }
        if(!limit)
            dp[pos][presum][lcm[preLcm]]=ans;
        return ans;
    }


    LL calu(LL x)
    {
        int pos=0;
        while(x)
        {
            bit[pos++]=x%(10LL);
            x/=(10LL);
        }
        return dfs(pos-1,0,1,true);
    }

    int main()
    {
        int t;
        scanf("%d",&t);
        Init();
        while(t--)
        {
            LL a,b;
            scanf("%I64d%I64d",&a,&b);
            printf("%I64d ",calu(b)-calu(a-1));
        }
        return 0;
    }
    * This source code was highlighted by YcdoiT. ( style: Codeblocks )
  • 相关阅读:
    GCC 里面的一些命令
    Servlet中的GET和POST之间的区别
    短暂的计算机职业生涯
    如何写详细设计文档
    exe4j打包jar文件成exe .
    android反编译问题
    OnScrollListener拖住主线程
    涵盖Android所有知识点的小实例
    屏蔽log
    sdk调低版本时,clean后失去R文件
  • 原文地址:https://www.cnblogs.com/CKboss/p/3350839.html
Copyright © 2011-2022 走看看