BZOJ-1833 [ZJOI2010]count 数字计数 数位DP

来源:互联网 发布:centos keepalived nfs 编辑:程序博客网 时间:2024/06/05 04:15

大家都很强, 可与之共勉。

1833: [ZJOI2010]count 数字计数

Time Limit: 3 Sec Memory Limit: 64 MB
Submit: 3440 Solved: 1518
[Submit][Status][Discuss]
Description

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

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

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

1 99

Sample Output

9 20 20 20 20 20 20 20 20 20

HINT

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

Day1

Day1

显然,我们可以先计算出[1,a-1],[1,b]中每个数字出现的个数,然后相减即可。问题变为:给一个数n,求1,2,…,n中每个数字出现的次数。
以下叙述中,“i位”都是从低位向高位数。
首先,我们允许前导0,也不考虑数大小的上限,在位数一定时,设为i,每种数字出现的次数是相同的,记为f(i)。不难得出递推式f(i)=10*f(i-1)+10^(i-1)(将第i位和前i-1位的数字分开考虑即可)。
继续,如果仍然允许前导0,但将数的上限考虑进来的话,我们只需要从高位向低位计算。设当前为第i位,n的这一位的数字为k,那么对于那些这一位数字小于k的数,他们的前i-1位是没有数的大小上限的,每个数字累加k*f(i-1)即可,同时0~k-1的数累加10^(i-1);而对于那些这一位的数字恰好为k的数,为了不超过上限,应累加前i-1位的数字形成的数+1。
最后,如何处理前导0呢?
其实,假设n有m位,我们只需要先统计出1,2,…m-1位数的情况(而这些是没有上限的,会比较方便),计算时而且保证这些数的首位不为0,然后对于m位同样的在最高位的时候不将0计算进来即可。

#include <cstdio>int num[20] ;long long a, b, f[20], pow[20] ;long long cnta[10], cntb[10] ;inline void Digit_Dp ( long long n, long long* cnt )  {    if ( !n )  return ;    long long N = n ;    int M ;    for ( M = 0 ; N ; num[++ M] = N % 10, N /= 10 ) ;    for ( int i = 1 ; i < M ; ++ i )  {        cnt[0] += f[i - 1] * 9 ;        for ( int j = 1 ; j < 10 ; ++ j )  cnt[j] += f[i - 1] * 9 + pow[i - 1] ;    }    n -= num[M] * pow[M - 1] ;    for ( int i = 1 ; i < num[M] ; ++ i )  cnt[i] += pow[M - 1] ;    for ( int i = 0 ; i < 10; ++ i )    cnt[i] += f[M - 1] * ( num[M] - 1 ) ;    cnt[num[M]] += n + 1 ;    for ( int i = M - 1 ; i ; -- i )  {        n -= num[i] * pow[i - 1] ;        for ( int j = 0 ; j < num[i] ; ++ j )  cnt[j] += pow[i - 1] ;        for ( int j = 0 ; j < 10; ++ j )    cnt[j] += f[i - 1] * num[i] ;        cnt[num[i]] += n + 1 ;    }}int main ( )  {    pow[0] = 1 ;    for ( int i = 1 ; i < 15 ; ++ i )  {        f[i] = f[i - 1] * 10 + pow[i - 1] ;        pow[i] = pow[i - 1] * 10 ;    }    scanf ( "%lld%lld", &a, &b ) ;    Digit_Dp ( a - 1, cnta ) ;    Digit_Dp ( b, cntb ) ;    for ( int i = 0 ; i <= 9 ; ++ i )        printf ( "%lld%c", cntb[i] - cnta[i], ( i ^ 9 ) ? ' ' : '\n' ) ;}
原创粉丝点击