////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
基础的数位dp
记忆化思路比较清晰
注意前导0的特判
递推拆分比较复杂,需要结合题意思考统计方式
(预处理无脑)
递推:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define int long long
const int N=14;
int f[N][N][N];
int t[N];
inline void pre(){
t[0]=1;
t[1]=10;
for(int i=2;i<=13;++i)t[i]=t[i-1]*10;
for(int i=0;i<10;++i)
f[1][i][i]=1;
//数位的预处理大多是从f[1][k][]...开始的
for(int i=2;i<=12;++i){
for(int j=0;j<10;++j){
for(int k=0;k<10;++k){
for(int p=0;p<10;++p){
f[i][j][p]+=f[i-1][k][p];
//不难想到状态转移方程
//注意站在当前状态,用之前可能的状态更新不同的数的总数
}
}
f[i][j][j]+=t[i-1];
//注意放在循环外面
}
}
}
int bit[N],ansl[N],ansr[N];
inline void getans(int n,int ans[]){
int len=0,res=n;
while(n){
bit[++len]=n%10;
n/=10;
}
// 预处理 前导零的情况 (最高位是0,那么所有讨论的子第一位都不能是0)
//如果忽略前导零,那么都当作len位数处理
//如果前导零有实际含义,就要单独处理1~len-1位数(相当于在前面加0)
//也可以放在下面处理,只是碰到0特判有点麻烦
for(int i=1;i<=len-1;++i){
for(int j=1;j<=9;++j){
for(int k=0;k<=9;++k){
ans[k]+=f[i][j][k];
}
}
}
for(int i=len;i>=1;--i){
for(int j=0;j<bit[i];++j){
if(i==len&&j==0)continue;//最高位是0的情况之前已经讨论过
for(int k=0;k<10;++k){
ans[k]+=f[i][j][k];
}
}
res%=t[i-1];//算出1~i-1位的值
ans[bit[i]]+=res+1;
//循环带来是结果:所有都是最高位被忽略
// 但由于题目的特性,会单独统计最高位的贡献,故最高位被忽略无影响。
//首先,当前j=bit[i],只能累加i-1位的权值总和。
//所以当前位的当前数没有被计入
//当前共有 0~[i-1][i-2][...][1]
//和预处理的10^(i-1)不同,此处通过取模得到的是最大值,
//从0开始,还需+1
}
}
//预处理的时候,用状态转移即可
//在拆分的时候一定要注意:
//细节太多:
//1. 注意循环的本质就保证了
signed main(){
pre();
int l,r;
scanf("%lld%lld",&l,&r);
getans(r,ansr);
getans(l-1,ansl);
for(int i=0;i<=9;++i){
printf("%lld ",ansr[i]-ansl[i]);
}
return 0;
}
记忆化:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=20;
int f[N][N][N],bit[N],l,r;
int DFS(int loc,int num,int sum,bool zero,bool up){
if(loc==0)return sum;
if(!up&&!zero&&f[loc][num][sum]!=-1)return f[loc][num][sum];
int maxnum= up? bit[loc]:9;
int res=0;
//ÏòÏÂ×ªÒÆ¡£
//°Ñ0µ¥¶ÀÄóöÀ´´¦Àí¡£
//Èç¹ûÇ°ÃæÃ»ÓÐǰµ¼0 ,»òÕß´Ë´¦ÊÇ×îºóһ룬¾Í²»ÓùÜ
if((!zero)||loc==1)
res+=DFS(loc-1,num,sum+(num==0),0,up&&0==bit[loc]);
else
res+=DFS(loc-1,num,sum, 1,up&&0==bit[loc]);
for(int i=1;i<=maxnum;++i){
res+=DFS(loc-1,num,sum+(num==i),0,up&&i==maxnum);
}
return ((!zero)&&(!up))?f[loc][num][sum]=res:res;
}
inline int getans(int n,int k){
if(n<0)return 0;
if(n==0)return k==0? 1:0;
int len=0;
while(n){
bit[++len]=n%10;
n/=10;
}
return DFS(len,k,0,1,1);
}
signed main(){
scanf("%lld%lld",&l,&r);
memset(f,-1,sizeof f);
for(int i=0;i<=9;++i){
printf("%lld ",getans(r,i)-getans(l-1,i));
}
return 0;
}