BZOJ_1833_[ZJOI2010]count 数字计数_数位DP

BZOJ_1833_[ZJOI2010]count 数字计数_数位DP

题意:

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

 

分析:

数位DP

f[i][j][k]表示i位数,以j开头的数中k出现的次数

预处理出来10的幂(在数位DP中经常会用到)

f[i][j][k]+=f[i-1][l][k]+(j==k)*10^i

之后按位枚举,0的情况特殊处理

 

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL long long
LL f[15][11][11],a,b,mi[15];
void init(){
    mi[1]=1;
    for(int i=2;i<=13;i++){
        mi[i]=mi[i-1]*10;
    }
    for(int i=0;i<=9;i++){
        f[1][i][i]=1;
    }
    for(int i=2;i<=13;i++){
        for(int j=0;j<=9;j++){
            for(int k=0;k<=9;k++){
                for(int l=0;l<=9;l++){
                    f[i][j][k]+=f[i-1][l][k];
                    if(j==k)f[i][j][k]+=mi[i-1];
                }
            }
        }
    }
}
LL calc(LL x,int p){
    if(!x)return (!p);
    LL re=0;
    int d=13;
    while(mi[d]>x)d--;
    //d++;
    for(int i=1;i<d;i++){
        for(int j=1;j<=9;j++){
            re+=f[i][j][p];
        }
    }
    if(!p)re++;
    int cur=x/mi[d];
    for(int i=1;i<cur;i++){
            re+=f[d][i][p];
    }
    x%=mi[d];
    if(cur==p)re+=x+1;
    for(int i=d-1;i;i--){
        cur=x/mi[i];
        for(int j=0;j<cur;j++){
            re+=f[i][j][p];
        }
        x%=mi[i];
        if(cur==p)re+=x+1;
    }
    return re;
}
int main(){
    scanf("%lld%lld",&a,&b);
    init();
    int flg=0;
    for(int i=0;i<=9;i++){
        if(!flg)flg=
            printf("%lld",calc(b,i)-calc(a-1,i));
        else printf(" %lld",calc(b,i)-calc(a-1,i));
    }
}

 

posted @ 2018-03-04 22:39  fcwww  阅读(212)  评论(0编辑  收藏  举报