[bzoj1833][ZJOI2010][count] (数位dp)

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。

Solution

十进制数位dp

设f(i,j,k)表示第i位以j开头的数含有多少个数字k

先预处理出f[i][j][k]数组

转移的时候从高到低转移

#include<stdio.h>
#define LL unsigned long long
int len,zt[33];
LL f[33][33][33],bt[33],a,b,ans[33];
void init() {
    bt[1]=1;
    for(int i=2; i<=13; i++)
        bt[i]=bt[i-1]*10;
    for(int i=0; i<10; i++)
        f[1][i][i]=1;
    for(int i=2; i<=13; i++)
        for(int j=0; j<10; j++)
            for(int k=0; k<10; k++) {
                for(int l=0; l<10; l++)
                    f[i][j][l]+=f[i-1][k][l];
                f[i][k][k]+=bt[i-1]; } }
void solve(LL x,LL d) {
    LL bef=x;
    for(len=0; x; x/=10)
        zt[++len]=x%10;
    for(int i=1; i<len; i++)
        for(int j=1; j<10; j++)
            for(int k=0; k<10; k++)
                ans[k]+=d*f[i][j][k];
    for(int i=len; i; i--) {
        for(int j=0; j<zt[i]; j++) {
            if(!j && i==len)continue;
            for(int k=0; k<10; k++)
                ans[k]+=d*f[i][j][k]; }
        ans[zt[i]]+=d*(bef%bt[i]+1); } }
int main() {
    init();
    scanf("%lld%lld",&a,&b);
    solve(b,1);
    solve(a-1,-1);
    for(int i=0; i<9; i++)
        printf("%lld ",ans[i]);
        printf("%lld\n",ans[9]);
    return 0; }

 

posted @ 2017-01-13 08:42  keshuqi  阅读(189)  评论(0编辑  收藏  举报