题意:
给出一段区间a-b,统计这个区间内0-9出现的次数。
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次。
Input
两个数a,b(1 <= a <= b <= 10^18)
Output
输出共10行,分别是0-9出现的次数
Input示例
10 19
Output示例
1 11 1 1 1 1 1 1 1 1
思路:
这道题是51Nod - 1009的进阶版,1009传送门:
点击打开链接
具体的思路,1009的那篇博客已经介绍得比较清楚,这道题对于求1到9其实都是和1009那题一样。
这题的难点其实是对于0的处理,因为求解的时候存在前导零,需要在参数中增加一个flag,来表示这里枚举的0是不是前导0,如果是则num不增加,如果不是则num+1。
另外,对于记忆化搜索的部分,0的处理也值得注意,如果需要保存的dp值必须得是在没有limit限制以及flag限制的情况下。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[50][15], ten[50], Hash[15];
int a[50];
void init() {
ten[0] = 1;
for (int i = 1; i <= 18; i++)
ten[i] = ten[i - 1] * 10;
}
ll dfs(int pos, int num, int x, int flag, int limit) {
if (pos == -1) return num;
if (x != 0 && !limit && dp[pos][x] != -1) return ten[pos + 1] * num + dp[pos][x];
if (x == 0 && flag && !limit && dp[pos][x] != -1) return ten[pos + 1] * num + dp[pos][x];
int up = limit ? a[pos] : 9;
ll res = 0;
if (x == 0) {
for (int i = 0; i <= up; i++) {
if (i == x) {
if (flag) res += dfs(pos - 1, num + 1, x, flag, limit && i == a[pos]);
else res += dfs(pos - 1, num, x, flag, limit && i == a[pos]);
}
else res += dfs(pos - 1, num, x, true, limit && i == a[pos]);
}
}
else {
for (int i = 0; i <= up; i++) {
if (i == x) res += dfs(pos - 1, num + 1, x, flag, limit && i == a[pos]);
else res += dfs(pos - 1, num, x, flag, limit && i == a[pos]);
}
}
if (x != 0 && !limit) dp[pos][x] = res;
else if (x == 0 && flag && !limit) dp[pos][x] = res;
return res;
}
ll solve(ll x, ll y) {
int pos = 0;
while (x) {
a[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1, 0, y, false, true);
}
void debug(ll n, ll m) {
for (int i = n; i <= m; i++) {
printf("%d\n", i);
int x = i;
while (x) {
Hash[x % 10]++;
x /= 10;
}
}
for (int i = 0; i < 10; i++)
printf("[%d] = %I64d\n", i, Hash[i]);
}
int main() {
init();
ll m, n;
scanf("%I64d%I64d", &n, &m);
// debug(n, m);
memset(dp, -1, sizeof(dp));
for (int i = 0; i < 10; i++)
printf("%I64d\n", solve(m, i) - solve(n - 1, i));
return 0;
}