• 51nod 1042 数位dp


    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1042

    1042 数字0-9的数量

    基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题
    收藏
    关注
    给出一段区间a-b,统计这个区间内0-9出现的次数。
    比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次。
    Input
    两个数a,b(1 <= a <= b <= 10^18)
    Output
    输出共10行,分别是0-9出现的次数
    Input示例
    10 19
    Output示例
    1
    11
    1
    1
    1
    1
    1
    1
    1
    1
    经典的数位dp题目,但是这个'0'真是搞得我恶心,第一次见是在玲珑某次比赛,那次一直怼这个最后爆零- -
    dp[i]表示[0,10
    i
    -1]之间所有的数里面0-9出现的次数,有dp[i]=10*dp[i-1]+pow(10,i-1),显然1-9的次数的一样的,0得话,
    因为没有以零开头的多位数,所以这里面是多计算了一部分'0'的,计算时遇到0就要想办法减去他才行。
    假如[0,999],多计算的就是100+10+1个零,对应的是001,002.....099,100是最高位,10是次高位,依次递推。
    假如要计算f(2049,0),第一次 s+=dp[3]*2;
    这两个dp[3]一个是加的 [0,999]一个是[1000,1999],但是我们只去掉一次就好了因为在[1000,1999]间那些零反而是我们需要的,就是这一点我糊涂好久。
     1 #include<cstring>
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<cmath>
     5 using namespace std;
     6 #define inf 0x3f3f3f3f
     7 #define LL  long long
     8 LL dp[25], zero[25] = { 0,0,10 };
     9 LL qpow(LL a,LL b,LL r=1){for(;b;b>>=1,a=a*a)if(b&1)r=r*a;return r;}
    10 void init()
    11 {
    12     dp[1]=1;
    13     for(LL i=2;i<19;++i)
    14         dp[i]=10*dp[i-1]+qpow(10,i-1);
    15 }
    16 LL f(LL N,LL digit)
    17 {
    18     if(N==0) {return digit?0:1;}
    19     int len=log(N+0.05)/log(10)+1;
    20     LL s=0,nx=N;
    21     for(int i=len;i>=1;--i)
    22     {
    23         LL mul=qpow(10,i-1);
    24         s+=dp[i-1]*(N/mul);
    25         {
    26         if(N/mul-1>=digit) s+=qpow(10,i-1);
    27         if(N/mul==digit) s+=N%mul+1;
    28         }
    29         N%=mul;
    30         //if(!digit) cout << "s=" << s << endl;
    31     }
    32     if (!digit)                                     //  删除前缀是0的结果
    33     {
    34         LL m = 1;
    35         while (nx)
    36         {
    37             s -= m;
    38             m *= 10;
    39             nx = nx / 10;
    40         }
    41     }
    42     //if (!digit) cout <<"s="<< s << endl;
    43     return s;
    44 }
    45 int main()
    46 {
    47     init();
    48     LL a,b,x;
    49     cin >> a >> b;
    50     for(x=0;x<=9;++x)
    51     {
    52         cout<<f(b,x)-f(a-1,x)<<endl;
    53     }
    54     return 0;
    55 }

        这是从新学习后自己写的代码

      

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define LL long long 
     4 LL f[25]={0,1};
     5 LL p10[25]={1,1};
     6 LL zero[25]={0,0};
     7 LL bit[25];
     8 void init(){
     9     for(int i=1;i<=20;++i) p10[i]=p10[i-1]*10;
    10     for(int i=2;i<=20;++i) zero[i]=zero[i-1]*10+10;
    11     for(int i=2;i<=20;++i) f[i]=f[i-1]*10+p10[i-1];
    12 }
    13 LL cal(LL N,int x){
    14     int len=0;
    15     while(N){
    16         bit[len++]=N%10;
    17         N/=10;
    18     }
    19     bit[len]=-1;
    20     LL ans=0,tot=0;
    21     for(int i=len-1;i>=0;--i){
    22         ans+=f[i]*bit[i];
    23         if(!x && i==len-1) ans-=(LL)(zero[i]);
    24         if(bit[i]>x && ((x==0&&i==len-1)==0) ) ans+=p10[i];
    25         ans+=p10[i]*bit[i]*tot;
    26         if(bit[i]==x) tot++;
    27     }
    28     return ans;
    29 }
    30 int main(){
    31     LL l,r,n,i,j,k;
    32     init();
    33     while(scanf("%lld%lld",&l,&r)==2){
    34         for(i=0;i<10;++i)
    35             printf("%lld
    ",cal(r+1,i)-cal(l,i));
    36     }
    37     return 0;
    38 }
  • 相关阅读:
    图书排列
    L1-059 敲笨钟 (20 分)
    区间移位
    取球博弈
    poj 2456 Aggressive cows
    对局匹配
    发现环
    数字划分
    哥德巴赫分解
    把数组排成最小的数
  • 原文地址:https://www.cnblogs.com/zzqc/p/7402907.html
Copyright © 2020-2023  润新知