真正的危机不是机器人像人一样思考,而是人像机器一样思考。 ——凉宫春日的忧郁

[ZJOI2010] 数字统计

[ZJOI2010] 数字统计

题目

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

INPUT

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

OUTPUT

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

SAMPLE

INPUT

1 99

OUTPUT

9 20 20 20 20 20 20 20 20 20

解题报告

第二道数位$dp$,找着点感觉了?

首先,我们预处理出来从低向高位数第$i$位数,每个数码出现的次数,递推式很简单

$$f[i]=10*f[i-1]+10^{i-1}$$

我们分两部分考虑即可,第$i$位为该数字的数有$10^{i-1}$个,后$i-1$位数该该数字出现的次数为$f[i-1]$,前面的数共有$10$种可能(允许前导0),故$f[i]=10*f[i-1]+10^{i-1}$

然后考虑如何统计答案。

对于低于该数位数的数,我们可以去除前导零,对上式进行变形(具体式子见代码),求和即可

对于位数等于该数位数的数,我们可以逐位枚举统计,利用处理出来的$f$数组和$10^{i}$进行转移即可

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 using namespace std;
 5 typedef long long L;
 6 L a,b,f[20],pw[20],ans[20];
 7 int num[40],top;
 8 inline void solve(L x,int flag){
 9     if(!x)return;
10     top=0;L ret(x);
11     while(x){num[++top]=x%10;x/=10;}
12     for(int i=1;i<top;++i)
13         for(int j=0;j<=9;++j)
14             ans[j]+=flag*(9*f[i-1]+(j?pw[i-1]:0));
15     for(int i=top;i;--i){
16         ret-=num[i]*pw[i-1];ans[num[i]]+=(ret+1)*flag;
17         for(int j=(i==top);j<num[i];++j)ans[j]+=pw[i-1]*flag;
18         for(int j=0;j<=9;++j)ans[j]+=f[i-1]*(num[i]-(i==top))*flag;
19     }
20 }
21 int main(){
22     freopen("countzj.in","r",stdin);freopen("countzj.out","w",stdout);
23     scanf("%lld%lld",&a,&b);
24     pw[0]=1;for(int i=1;i<=13;++i)f[i]=10*f[i-1]+pw[i-1],pw[i]=pw[i-1]*10;
25     solve(b,1);solve(a-1,-1);
26     printf("%lld",ans[0]);for(int i=1;i<=9;++i)printf(" %lld",ans[i]);
27 }
View Code

 

posted @ 2017-11-01 14:47  Hzoi_Mafia  阅读(265)  评论(0编辑  收藏  举报
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。 ——死神