#sWDPybttg050307. 4.数字计数

4.数字计数

No testdata at current.

好的,我将这道题整理为清晰的题面格式,并补充样例解释、数据范围与时空限制:


题目描述

原题来自:ZJOI 2010

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


输入格式

仅包含一行两个整数 a,ba, b,含义如上所述。


输出格式

包含一行 1010 个整数,分别表示数码 090\sim 9[a,b][a,b] 中出现了多少次。


样例

样例输入

1 99

样例输出

9 20 20 20 20 20 20 20 20 20

样例解释

[1,99][1,99] 中:

  • 数码 00 出现次数:10,20,30,,9010,20,30,\dots,9099 次(00 在个位出现 99 次);
  • 数码 11 出现次数:
    • 个位:1,11,21,,911,11,21,\dots,911010
    • 十位:10,11,12,,1910,11,12,\dots,191010 次 合计 2020 次;
  • 数码 292\sim 9 同理,各出现 2020 次。

数据范围

  • 对于 30%30\% 的数据,1ab1061 \le a \le b \le 10^6
  • 对于 100%100\% 的数据,1ab10121 \le a \le b \le 10^{12}

时空限制

  • 时间:1000 ms1000 \text{ ms}
  • 内存:524288 KB524288 \text{ KB}

提示
此题为经典的 数位 DP 问题,需要分别统计 090\sim 9 每个数码的出现次数。

方法: 我们可以分别对每个数码 dd090\sim 9)计算它在 [1,n][1,n] 中出现的次数 count(d,n)count(d,n),然后答案即为 count(d,b)count(d,a1)count(d,b) - count(d,a-1)

对于固定的 dd,设计状态:dp[pos][sum][lead][limit]dp[pos][sum][lead][limit] 表示:

  • pospos:当前处理到的位(从高到低);
  • sumsum:当前数码 dd 已经出现的次数;
  • leadlead:是否前面全是前导零;
  • limitlimit:是否受到 nn 的上限限制。

转移时,枚举当前位数字 curcur0099(若有限制则到 digit[pos]digit[pos]),更新:

  • 新的 sum=sum+(cur==d)sum' = sum + (cur == d),但要注意如果 leadlead 为真且 cur=0cur=0,则 d=0d=0 时不计入(因为前导零不算作数字 00)。
  • 新的 lead=leadlead' = leadcur==0cur==0

最终当 pospos 到达末尾时,返回 sumsum

记忆化:dp[pos][sum][lead]dp[pos][sum][lead],其中 limitlimit 不记忆(但 limit=0limit=0 时才记忆化)。

复杂度:每个数码 O(位数×位数×2)O(\text{位数} \times \text{位数} \times 2),总 O(10×(log10b)3)O(10 \times (\log_{10} b)^3),可以接受。

优化:可以一次 DP 同时统计所有数码,状态为 dp[pos][cnt0,,cnt9]dp[pos][cnt_0,\dots,cnt_9],但状态数太大,不可行。因此分别计算每个数码是合理的。

注意:数码 00 需要特殊处理前导零。

另一种思路:利用组合数学直接计算每个数码在每个数位上的贡献,可以做到 O(log10b)O(\log_{10} b) 计算一个数码。
设当前处理第 kk 位(从低位到高位,权重 10k110^{k-1}),将数字分为三部分:高位 highhigh、当前位 curcur、低位 lowlow

  • 如果 cur>dcur > d,则该位出现 dd 的次数为 (high+1)×10k1(high+1) \times 10^{k-1}
  • 如果 cur=dcur = d,则该位出现 dd 的次数为 high×10k1+low+1high \times 10^{k-1} + low + 1
  • 如果 cur<dcur < d,则该位出现 dd 的次数为 high×10k1high \times 10^{k-1}

对于 d=0d=0 的情况需要特殊处理,因为不能有前导零:当 high=0high=0 时,当前位不能算作 00 的出现,需要减去 10k110^{k-1}

这种方法更快,且不需要 DP。