• BZOJ1833 [ZJOI2010] count


    【问题描述】

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

    【输入格式】

    输入文件中仅包含一行两个整数a、b,含义如上所述。

    【输出格式】

    输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

    【输入样例】

          1 99

    【输出样例】

          9 20 20 20 20 20 20 20 20 20

    【数据范围】

    30%的数据中,a<=b<=10^6;
    100%的数据中,a<=b<=10^12。

    正解:数位DP

    解题报告:

    只要想清楚就很简单,首先问题肯定是把区间1-b和区间1-(a-1)出现的数码统计出来,相减输出来;暴力一个一个数的统计肯定是会超时的,我们先不管前导0,然后我们把数字分为n位,前n位出现的所有数码次数可以看出全是一样的,f[i]表示前i位出现的数码次数,f[i]=f[i-1]*10+10^(i-1);接下来,如果仍然允许前导0,但将数的上限考虑进来的话,我们只需要从高位向低位计算。设当前为第i位,数字的这一位的数字为k,那么对于那些这一位数字小于 k的数,他们的前i-1位是没有数的大小上限的,每个数字累加k*f(i-1)即可,同时0~k-1的数累加10^(i-1);而对于那些这一位的数字恰 好为k的数,为了不超过上限,应累加前i-1位的数字形成的数+1(全为0); 最后,我们来想一下如何处理不允许前导0的情况,其实我们可以一位一位算,因为前n-1位没有上限,第i位可以直接通过f[i-1]*9算出,再把1-9加上10^(i-1),这样即可保证没算包括前导0的情况。

     1 #include <iostream>
     2 #include <iomanip>
     3 #include <cstdlib>
     4 #include <cstdio>
     5 #include <cmath>
     6 #include <algorithm>
     7 #include <string>
     8 #include <cstring>
     9 #define RG register
    10 #define ll long long
    11 
    12 using namespace std;
    13 
    14 ll f[20],l[10],r[10];
    15 
    16 void solve(ll *x,ll s){
    17     if (!s) return;
    18     ll k=s,m;RG int w=0,i,j,sum[15];
    19     while(k) sum[++w]=k%10,k/=10;
    20     for (m=1,i=1; i<w; i++){
    21         x[0]+=f[i-1]*9;
    22         for (j=1; j<=9; j++) x[j]+=f[i-1]*9+m;
    23         m*=10;
    24     }
    25     k=s-sum[w]*m;
    26     for (i=1; i<sum[w]; i++) x[i]+=m;
    27     for (i=0; i<=9; i++) x[i]+=f[w-1]*(sum[w]-1);
    28     x[sum[w]]+=k+1;
    29     for (i=w-1; i; i--){
    30         m/=10,k-=sum[i]*m;
    31         for (j=0; j<sum[i]; j++) x[j]+=m;
    32         for (j=0; j<=9; j++) x[j]+=f[i-1]*sum[i];
    33         x[sum[i]]+=k+1;
    34     }
    35     return;
    36 }
    37 
    38 int main(){
    39     freopen("input.txt","r",stdin);
    40     freopen("output.txt","w",stdout);
    41     ll a,b,s;RG int i;
    42     f[1]=1;s=10;
    43     for (i=2; i<=15; i++) f[i]=f[i-1]*10+s,s*=10;
    44     scanf("%lld%lld",&a,&b);
    45     for (i=0; i<10; i++) l[i]=r[i]=0;
    46     solve(l,a-1),solve(r,b);
    47     printf("%lld",r[0]-l[0]);
    48     for (i=1; i<=9; i++) printf(" %lld",r[i]-l[i]);
    49     printf("
    ");
    50     return 0;
    51 }
  • 相关阅读:
    Java学习(运算符,引用数据类型)
    Java学习(基本语句,语法,变量)
    Java学习(简介,软件安装)
    MySQL连接查询(多表查询)
    MySQL数据约束
    VS code MacOS 环境搭建
    三维空间中xoy平面上特定抛物线的正等测投影解析解的一种求法
    抛物线正等测投影的解析解求法
    抛物线正等测投影数值解的求法
    反向工程“你的使用说明书”小记
  • 原文地址:https://www.cnblogs.com/cjk2001/p/6366310.html
Copyright © 2020-2023  润新知