正题
这题我们看道题面,看上去很简单,但是如果暴力枚举的时间复杂度是很高的。
所以我们就会想到用数位Dp来去求解。
关于数位Dp,其实是一种处理数字局限性的计算方法。
关键就在于怎么样让这个“局限性”体现出来的同时,加快我们程序运行的速度。
那么对于这题,它要求解的目标是每个数码有多少个。
我们会想到00~99处理出来的数字都是一样的,在此中0有10个,1有20个……
所以对于2XX,和3XX,他们后面的数位是不用重复求解的,这是我们可以把它们存起来。
定义solve[i]表示填到第i位到最后一位的状态总和(只计算000..0~999..9)是多少。
当前这个位置的数码,也要进行存储,如果后面可以填000~999那么就是k*10^n(其中k为当前枚举的数字,10^n表示这一位到最后共有n位),否则如果后面的数有限制(比如说547,当前枚举到5这一位,后面只能填00~47),那么5就会被计算(47+1)=48次,所以还要预处理一个后缀和g。
然后就完成了这一道大水题。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
long long x,y;
long long solve[100];//无限制且没有前导0时第i位到最后一位的状态总和
long long val[100];//表示第i位的值
long long ci[100];//表示10的i次方
long long g[100];/表示后缀
long long Dp(int pos,int tf,int k,int all){
if(pos==0) return 0;//枚举完就返回,不产生价值
if(!tf && !all && solve[pos]!=-1) return solve[pos];//如果已经处理过就返回已经处理过的值
long long res=0;//否则就计算
int end=tf?val[pos]:9;//枚举的上限根据前面的有没有“满”来决定
for(int i=0;i<=end;i++) {
res+=Dp(pos-1,tf&&i==end,k,all&&i==0);//往下搜
if(!(all && i==0) && i==k) res+=((i==end&&tf)?g[pos-1]+1:ci[pos-1]);//如果前面没有前导零而且i等于当前所求解的k数码,那么他就会
}//前所求解的k数码,那么他就会带来相应的价值
if(!all && !tf) solve[pos]=res;//记忆化
return res;
}
long long gett(long long v,int k){
memset(solve,-1,sizeof(solve));
int len=0;
while(v!=0){
val[++len]=v%10;
v/=10;
g[len]=g[len-1]+val[len]*ci[len-1];//预处理后缀
}
return Dp(len,1,k,1);
}
int main(){
ci[0]=1;
for(int i=1;i<=15;i++) ci[i]=ci[i-1]*10;
long long tot=0;
scanf("%lld %lld",&x,&y);
x--;
for(int i=0;i<=9;i++)//根据每一个数码进行求解
printf("%lld ",gett(y,i)-gett(x,i));
}