数码
链接:https://ac.nowcoder.com/acm/problem/13221
题解:https://ac.nowcoder.com/discuss/399522
题目描述
给定两个整数
l
l
l 和
r
r
r ,对于所有满足
1
≤
l
≤
x
≤
r
≤
1
0
9
1 ≤ l ≤ x ≤ r ≤ 10^9
1≤l≤x≤r≤109 的
x
x
x ,把
x
x
x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
输入描述:
一行,两个整数
l
l
l 和
r
r
r
(
1
≤
l
≤
r
≤
1
0
9
)
(1 ≤ l ≤ r ≤ 10^9)
(1≤l≤r≤109)。
输出描述:
输出 9 行。
第
i
i
i 行,输出数码
i
i
i 出现的次数。
思路:
整数分块。计算
[
1
,
r
]
[1,r]
[1,r] 数字1~9出现的次数,减去
[
1
,
l
−
1
]
[1,l-1]
[1,l−1] 数字1~9出现的次数。
区间
[
1
,
R
]
[1,R]
[1,R] 中的任意数
x
x
x 的因子是成对出现的,我们把它变成
a
×
b
a\times b
a×b 的形式(保证
a
<
b
a<b
a<b 避免重复取值,
a
≤
R
a\leq\sqrt R
a≤R),枚举
a
a
a,则
b
b
b 在
[
1
,
R
]
[1,R]
[1,R] 的可取值范围就是
[
a
+
1
,
⌊
R
/
a
⌋
]
[a+1,\lfloor{R/a}\rfloor]
[a+1,⌊R/a⌋],并且
b
b
b 是连续的,只需要把这个范围内的数分别对应处理最高位即可,这里就分块,同样
a
a
a 用到的次数是
b
b
b 的次数,并且还多
a
×
a
a\times a
a×a 的一次。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l, r, a[10], b[10];
ll high(ll x)
{
while (x >= 10)
x /= 10;
return x;
}
void calc(ll r, ll *arr)
{
for (ll a = 1; a * a <= r; a++) //枚举因子a
{
ll b = r / a; //a固定时因子b的范围[a+1,b]
for (ll i = 1; i <= r; i *= 10) //枚举因子b的位数
for (ll j = 1; j <= 9; j++) //枚举因子b的最高位
{
ll L = max(j * i, a + 1); //在i和j的范围下因子b的左边界
ll R = min((j + 1) * i - 1, b); //b的右边界
if (R - L >= 0)
arr[j] += R - L + 1;
}
ll x = high(a);
arr[x] += b - a + 1; //a对应b的范围[a+1,b], 外加一次a*a, 用了 b-a+1 次
}
}
int main()
{
cin >> l >> r;
calc(r, a);
calc(l - 1, b);
for (int i = 1; i <= 9; i++)
cout << a[i] - b[i] << endl;
return 0;
}
第二种写法:
看个例子就好了,比如
n
u
m
=
9
num=9
num=9 那么最后决定答案的是什么?不就是
[
9
]
,
[
90
,
99
]
,
[
900
,
999
]
,
[
9000
,
9999
]
.
.
.
.
.
.
[9],[90,99],[900,999],[9000,9999]......
[9],[90,99],[900,999],[9000,9999]...... 在范围内的倍数如
[
9
,
18
,
27
,
36......
]
,
[
90
,
180
,
270
,
360
,
.
.
.
.
.
.
,
91
,
182
,
273
,
.
.
.
.
.
.
,
92
,
.
.
.
.
.
.
]
,
[
900
,
.
.
.
.
.
.
]
,
.
.
.
.
.
.
[9,18,27,36......],[90,180,270,360,......,91,182,273,......,92,......],[900,......],......
[9,18,27,36......],[90,180,270,360,......,91,182,273,......,92,......],[900,......],......,这些倍数就是由前面区间中的数作为因子得到的
x
x
x,比如
R
=
100900
R=100900
R=100900,在
[
901
,
902
,
.
.
.
,
909
]
[901,902,...,909]
[901,902,...,909] 的符合的倍数的个数都为
111
111
111 个,所以就有
111
×
9
111\times 9
111×9 个最高位为
9
9
9 的因子,然后就可以把这个区间一起算,然后继续往下找倍数的个数相同的因子区间。(https://blog.nowcoder.net/n/16f97f30e14f428d8c3103b8ee0df34e)
Code2:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll calc(ll x, ll h) //右边界x,最高位h
{
ll res = 0;
for (ll t = 1; t <= x / h; t *= 10) //枚举最高位是h的位数
{
ll L = h * t, R = min(x, L + t - 1); //最高位是h的因子 [L,R]
for (ll i = L, j; i <= R; i = j + 1)
{
j = min(x / (x / i), R);
res += (j - i + 1) * (x / i); //范围为[i,j]内的因子,在范围内的倍数个数为x/i个
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
ll l, r;
cin >> l >> r;
for (int i = 1; i <= 9; i++)
cout << calc(r, i) - calc(l - 1, i) << endl;
return 0;
}