BSOJ: 2697 -- 【ZJOI2010】数字计数

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。
  
很裸的数位DP,好久都没刷过题了,居然搞了半小时。
我开了四维的数组,不知道还能不能优化。
p是要计算出现个数的数字 09
sum是之前这个数字出现次数。
flag1是判断之前有没有非0数,主要是为了针对0的计算。
建议人工模拟下,更能理解。
代码如下:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
long long F[20][10][15][2]; 
int c[100];
long long Cal(int x,int p,int sum,int flag1,int flag){
    if(!x)return sum;
    if(!flag&&F[x][p][sum][flag1])return F[x][p][sum][flag1];
    int k=flag?c[x]:9;
    long long ret=0;
    for(int i=0;i<=k;i++){
        ret+=Cal(x-1,p,sum+((i==p)&&((flag1&&p==0)||(p!=0))),flag1||(i!=0),flag&&(i==k));
    }
    return flag?ret:F[x][p][sum][flag1]=ret;
}
long long Ask(long long x,int k){
    c[0]=0;
    while(x){c[++c[0]]=x%10;x/=10;}
    return Cal(c[0],k,0,0,1);
}
int main(){
    long long a,b;
    scanf("%lld%lld",&a,&b);
    for(int i=0;i<=9;i++){
        printf("%lld ",Ask(b,i)-Ask(a-1,i));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值