[ZJOI2010] 数字计数

题目描述

给定两个正整数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/

posted @ 2018-09-03 19:55  real_l  阅读(433)  评论(0编辑  收藏  举报