(color{#0066ff}{ 题目描述 })
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
(color{#0066ff}{输入格式})
输入文件中仅包含一行两个整数a、b,含义如上所述。
(color{#0066ff}{输出格式})
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
(color{#0066ff}{输入样例})
1 99
(color{#0066ff}{输出样例})
9 20 20 20 20 20 20 20 20 20
(color{#0066ff}{数据范围与提示})
30%的数据中,(aleq bleq 10^6);
100%的数据中,(aleq bleq 10^{12})。
(color{#0066ff}{ 题解 })
一道基本的数位DP
本来以前做过的,代码太不清真了qwq,改一下
(f[i][j][k])为长度为i,最高位为j,k出现的次数
#include<bits/stdc++.h>
using LL = long long;
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
LL f[25][10][10], base[16];
LL ans1[10], ans2[10];
LL l, r, a[20], num, b[20];
void init() {
base[0] = 1;
for(int i = 1; i <= 13; i++) {
base[i] = base[i - 1] * 10;
for(int j = 0; j <= 9; j++)
for(int k = 0; k <= 9; k++) {
for(int l = 0; l <= 9; l++)
f[i][j][l] += f[i - 1][k][l];
if(j == k) f[i][j][j] += base[i - 1]; //最高位不动,后面随意的贡献
}
}
}
void query(LL p, LL *ans) {
num = 0;
while(p) {
a[++num] = p % 10;
b[num] = b[num - 1] + base[num - 1] * (p % 10); //从num位开始的数
p /= 10;
}
for(int i = 1; i < num; i++)
for(int j = 1; j <= 9; j++)
for(int k = 0; k <= 9; k++)
ans[k] += f[i][j][k]; //不贴边的
for(int i = 1; i < a[num]; i++) //无前导零的贴边
for(int j = 0; j <= 9; j++)
ans[j] += f[num][i][j];
ans[a[num]] += b[num - 1]; //作为某一位的贡献(后面什么数,最高位不变,统计贡献)
for(int i = num - 1; i >= 1; i--) {
for(int j = 0; j < a[i]; j++)
for(int k = 0; k <= 9; k++) //贴边统计
ans[k] += f[i][j][k];
ans[a[i]] += b[i - 1];
}
}
int main() {
init();
l = in(), r = in();
query(l, ans1);
query(r + 1, ans2);
for(int i = 0; i <= 9; i++) printf("%lld%c", ans2[i] - ans1[i] , i == 9? '
' : ' ');
return 0;
}