题目链接:https://ac.nowcoder.com/acm/problem/13221
题意:给定两个整数 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 1~9 1~9每个数码出现的次数。
思路:
在讲这一题得写法之前,我们先要引入整除分块这个知识。
整除分块是求
∑
i
=
1
n
n
/
i
\sum_{i=1}^{n}n/i
∑i=1nn/i的和。
首先如果我们直接
f
o
r
for
for循环遍历一遍,那一般题目一定会超时。还是遵循遇事不决先打表的原则,我们将
n
=
10
n=10
n=10带进去模拟一下,可以得到如下表所示的值:
我们可看到有很多的之都是一样的,那我们是不是就可以将所有
n
/
i
n/i
n/i相等的放在“一块”呢?那我们可以定义两个变量
l
,
r
l,r
l,r,用
l
l
l表示这一块的左区间,
r
r
r表示这一块的右区间,所以
l
l
l我们要从一开始遍历,每次令
l
=
r
+
1
l = r + 1
l=r+1,那
r
r
r呢?我们在草稿纸上算一下就知道,
r
=
n
/
(
n
/
l
)
r = n / (n / l)
r=n/(n/l),
n
/
l
n/l
n/l便是这个块内的值。所以这一块对答案的贡献就是
(
r
−
l
+
1
)
∗
(
n
/
l
)
(r-l+1)*(n/l)
(r−l+1)∗(n/l)
具体代码实现如下:
for(int l = 1 , r ; l <= x ; l = r + 1) {
r = x / (x / i );
ans += 1LL * (r - l + 1) * (x / l);
}
讲完了整除分块,我们回到原题。
题目是让我们求的数是
l
,
r
l,r
l,r这个区间的,假设函数
s
o
l
v
e
(
x
)
solve(x)
solve(x)表示
[
1
,
x
]
[1,x]
[1,x]这个区间对应得答案,那么
[
l
,
r
]
[l,r]
[l,r]区间的答案是不是就是
s
o
l
v
e
(
r
)
−
s
o
l
v
e
(
l
−
1
)
!
solve(r)-solve(l-1)!
solve(r)−solve(l−1)!那我们只要把这个
s
o
l
v
e
(
x
)
solve(x)
solve(x)写出来不就好啦!!!可以该怎么写呢?
因为题目要求我们求
1
−
>
9
1->9
1−>9出现的个数,所以我们可以一个一个的算,拿
1
1
1举例,如果
x
%
y
=
=
0
x\%y == 0
x%y==0
其中
y
∈
[
1
,
2
)
,
[
10
,
20
)
,
[
100
,
200
)
,
[
1000
,
2000
)
y\in[1,2),[10,20),[100,200),[1000,2000)
y∈[1,2),[10,20),[100,200),[1000,2000)那答案对1的贡献++,那我们就可以细化到每个区间(用
l
,
r
l,r
l,r表示)。
那
[
1
,
x
]
[1,x]
[1,x]区间有多少个数可以整除
y
y
y呢?答案显然就是
x
/
y
x/y
x/y;
那这一题就是让我们求
(
x
/
1
)
+
(
x
/
10
+
x
/
11
+
x
/
12
+
.
.
.
)
(x/1) + (x/10 + x/11 + x/12 + ...)
(x/1)+(x/10+x/11+x/12+...)这不就是我们上面讲的整除分块的板子题了嘛。
AC代码如下:
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define pii pair<int,int>
#define sd(x) scanf("%d",&x)
#define slld(x) scanf("%lld",&x)
#define pd(x) printf("%d\n",x)
#define plld(x) printf("%lld\n",x)
#define rep(i,a,b) for(int i = a ; i <= b ; i++)
#define per(i,a,b) for(int i = b ; i >= a ; i--)
#define mem(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define fast_io ios::sync_with_stdio(false)
const LL INF = 1e18;
const int mod = 2010;
const int maxn = 2e5 + 7;
LL solve(LL x,LL v) {
LL res = 0;
for(LL temp = 1 ; temp <= x / v ; temp *= 10) {
///确定左右区间
LL l = temp * v , r = min(x , l + temp - 1);
///整除分块
for(int i = l , j ; i <= r ; i = j + 1) {
j = min(x / (x / i) , r);
res += 1LL * (j - i + 1) * (x / i);
}
}
return res;
}
int main() {
int x,y;
while(~scanf("%d%d",&x,&y)) {
for(int i = 1 ; i <= 9 ; i++) {
///直接俄统计答案
printf("%lld\n" , solve(y,i) - solve(x-1,i));
}
}
return 0;
}