首页 > 代码库 > BZOJ1833 [ZJOI2010] count

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("\n");
50     return 0;
51 }

 

BZOJ1833 [ZJOI2010] count