题目描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
输入输出格式
输入格式:
输入文件中仅包含一行两个整数a、b,含义如上所述。
输出格式:
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
输入输出样例
输入样例#1:
1 99
输出样例#1:
9 20 20 20 20 20 20 20 20 20
说明
30%的数据中,\(a<=b<=10^6;\)
100%的数据中,\(a<=b<=10^{12}。\)
Solution
bzoj1883
数位dp,记忆化搜索
主要就是解释一下变量
len指当前搜到哪一位,当len=0时,就返回当前搜到的值
f表示当前前导0的条件合不合法,初始值是有前导0的,不合法,所以为0
limit表示当前搜索的这一位有没有限制,即不能超过原数这一数位上的数字,1表示有限制,0表示没有
sum表示有多少当前已经统计了多少查找的数字
tq表示当前查找的数字是什么
本来正常情况下,dp数组是要开四维的,\(dp[len][f][limit][sum]\),但是由于本题只在没有前导0以及当前位所有数字都搜满的情况下也就是(\(f\) && \(!limit\)),所以可以省去这两维
提示:关于数组大小,因为我们统计的数字做多只有15位,所以第1维开16即可,第二维因为每次只统计一个数字x,所以最好情况下就是每一位都是x,最多也就是15位,所以也只要开16
Code
#define lol long long
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
using namespace std;
const int N=20;
lol dp[N][N];
int bit[N];
void in(lol &ans)
{
ans=0;lol f=1; char i=getchar();
while(i<'0'||i>'9'){if(i=='-') f=-1; i=getchar();}
while(i>='0'&&i<='9') ans=(ans<<3)+(ans<<1)+i-'0',i=getchar();
ans*=f;
}
lol dfs(int len,int f,int limit,lol sum,int tq,lol ans=0) {
if(!len) return sum;
if(!limit && f && dp[len][sum]!=-1) return dp[len][sum];
int maxn=limit?bit[len]:9;
for(int i=0;i<=maxn;i++)
ans+=dfs(len-1,f||i,limit && i==maxn,sum+((f||i) && (i==tq)),tq);
if(!limit && f) dp[len][sum]=ans;
return ans;
}
lol solve(lol a,int tq,int k=0) {
memset(dp,-1,sizeof(dp));
while(a) {
bit[++k]=a%10;
a/=10;
}
return dfs(k,0,1,0,tq);
}
int main()
{
lol a,b; in(a),in(b);
for(int i=0;i<10;i++)
printf("%lld ",solve(b,i)-solve(a-1,i));
puts("");
return 0;
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
原文地址:https://www.cnblogs.com/real-l/p/9580501.html
时间: 2024-11-04 00:29:44