• Poj-3286- How many 0's?


    How many 0's?

    Description

    A Benedict monk No.16 writes down the decimal representations of all natural numbers between and including m and nm ≤ n. How many 0's will he write down?

    Input

    Input consists of a sequence of lines. Each line contains two unsigned 32-bit integers m and nm ≤ n. The last line of input has the value of m negative and this line should not be processed.

    Output

    For each line of input print one line of output with one integer number giving the number of 0's written down by the monk.

    Sample Input

    10 11
    100 200
    0 500
    1234567890 2345678901
    0 4294967295
    -1 -1

    Sample Output

    1
    22
    92
    987654304
    3825876150

      题意简单分析:给你两个数a和b,求从a手写到b,总共需要写多少个零(没有前置零)。先求出0到b需要的数num1,再求出0到a-1需要的数num2,用num1减去num2就是答案。

      问题可以递归分解成子问题,一步步简化处理;例如9287,拆分成五段来求零出现的次数——0-999,     1000-8999,9000-9199,    9200-9279,    9280-9287;

      chart数组,chart[i]表示从自然数0到i位(999..99)所写下的0 的个数(都是自然数不存在前置零)!上面的栗子中,第一段直接可以调用chart[]数组得出结果。(用chart[]省时,省步骤!第一次写没法推出chart数组,就暴力跑了一次;第二次,我觉得打表不是多稳,于是又花了两个小时坚持去推chart[]表,公式起始很简单,受cnt函数的影响,我起初一直仿照推下面cnt函数的方式去推——一直无果!后来,仔细跳出了推cnt的思路出来想想,起始就一行就够了!)

      dp数组,dp[i]表示从i位的(0...0)写到i位的(9...9)需要的0的个数,相当于给你i位数求这i位数的可能的所有组合(含前置零);接着上面的栗子中,第二段中,1000-8999可以看做1xxx,2xxx,3xxx,...,8xxx(xxx表示三位数的所有可能的组合个数);第三段,9000-9199, 可以看做90xx,91xx (xxx表示两位数的所有可能的组合个数);依次类推。

      fact数组,fact[i]数组存10的i 次幂= i位数的用0-9的全排列结果。

      建立上述三个数组以后,从高位向低位按照乘法和加法原则进行迭代求和即可。

    #include<stdio.h>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    #define eps 1e-10
    ll dp[11],fact[12];
    ll chart[20];
    ll arr[20]={0};  //由低位爬向高位
    
    ll f3(ll a[],int i,int j){  //计算数组a[]第i到第j位构成的数字,如9086,f3(1,2)返回86
        ll s=0;
        if(i<j)swap(i,j);
        for(;i>=j;i--){
            s=s*10+a[i];
        }
        return s;
    }
    
    void init()
    {
        fact[1]=10;  //存10的次幂
        for(int i=2;i<=10;i++)
            fact[i]=fact[i-1]*10;
    
         dp[1]=1;dp[2]=20;
        for(int i=3;i<=10;i++)
            dp[i]=fact[i-1]+10*dp[i-1];   //表示从i位的(0...0)写到i位的(99..9)需要的0的个数
    
    
        chart[1]=1;
        for(int i=2;i<=9;i++)    //表示从0到共i位的(99..99)需要写的0的个数
            chart[i]=chart[i-1]+dp[i-1]*9;
    
        return ;
    }
    
    ll cnt(int pos,int len)  //返回自然数0 到 n的含零数
    {
        ll ans=0;
        if(pos==len&&len>1){ //pos为最高位
                ans+=(arr[pos]-1)*dp[pos-1]+ chart[pos-1]+ cnt(pos-1,len);
        }
        else if(pos==len&&len==1){  //整个N只是个位数时
            ans=1;
        }
        else if(pos!=1){  //当不是最高位和最后一位时
            if(arr[pos]==0){
                ans+= f3(arr,1,pos) +cnt(pos-1,len)+1; //1表示pos位0时
            }
            else{
                ans+= arr[pos]*dp[pos-1]+ cnt(pos-1,len)+fact[pos-1];  //fact[i-1]表示该为为0时
            }
        }
        else{   //最后一位时
             if(arr[pos]==0){
                ans+=1;
            }
            else{
                ans+=1;  //最后一位的0
            }
        }
       // printf("pos=%d ans=%d
    ",pos,ans);
        return ans;
    }
    ll solve(ll n)   //主要就是把n进行数位分离,然后调用cnt统计
    {
        if(n<0)return (ll)0;
        if(n==0)return 1;
        ll ans=0,len;
        len=(ll)((int)log10(n*1.0)+1);//计算n的位数
    
        int i=1;
        while(n>0)
        {
            arr[i++]=n%10;n/=10;
        }
        ans=cnt(len,len);
        return ans;
    }
    
    int main()
    {
        ll m,n;
        init();
    
        while(scanf("%lld%lld",&m,&n)!=EOF)   //题意:m<=n
        {
            if(n==-1&&m==-1)break;
            printf("%lld
    ",solve(n)-solve(m-1));
        }
    
        return 0;
    }
         //打表代码,大概等10秒钟可以跑完
    /*void creat_chart(){
        freopen("output.txt","w",stdout);
        ll ss=0;int k=1;
        for(ll i=0;i<=(ll)fact[9];i++){
           ll  j=i;
           if(j==0)ss++;
            while(j>0){
                if(j%(ll)10==(ll)0)ss++;
                j/=(ll)10;
            }
            if(i==fact[k]-1){
                printf("0到%lld的含0数为: %lld
    ",i,ss);k++;
            }
        }
        printf("OK
    ");
        fclose(stdout);
    }*/
    /*得到chart【】表格 结果如下:
    0到9的含0数为: 1
    0到99的含0数为: 10
    0到999的含0数为: 190
    0到9999的含0数为: 2890
    0到99999的含0数为: 38890
    0到999999的含0数为: 488890
    0到9999999的含0数为: 5888890
    0到99999999的含0数为: 68888890
    0到999999999的含0数为: 788888890
    OK*/
    View Code

    自己动手推推,看是肯定看不懂的!

    用乘法和加法原则自己动手推推,很重要!反正你也看不懂!/斜眼笑

  • 相关阅读:
    【BZOJ3261】— 最大异或和(可持久化0/1Trie)
    【ZJOI2007】—捉迷藏(动态点分治)
    【HNOI2010】-城市建设(动态最小生成树)
    【BOI2007】Mokia 摩基亚
    【BZOJ 3262】-陌上花开(CDQ分治+树状数组)
    Win64 驱动内核编程-24.64位驱动里内嵌汇编
    Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook
    Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook
    Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
    Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)
  • 原文地址:https://www.cnblogs.com/zhazhaacmer/p/8378704.html
Copyright © 2020-2023  润新知