题目陈述
- 大意:求解下面的表达式的值
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \cfrac{n}{i}\rfloor i=1∑n⌊in⌋
算法一:朴素算法
算法思路
- 暴力算法,枚举每个 i i i,计算其对答案的贡献,遍历所有的 i i i即可
代码实现
class Solution {
public:
int work(long long n) {
long long ans = 0;
for (int i = 1; i <= n ; i ++ ) //枚举所有的i
ans += n / i; //计算i对答案的贡献
ans %= 998244353; //对答案取模
return ans;
}
};
复杂度分析
- 时间复杂度,所有的 i i i都遍历了一遍,时间复杂度为 O ( n ) O(n) O(n)
- 空间复杂度,定义了变量 a n s ans ans,为 O ( 1 ) O(1) O(1)
算法二:整除分块
算法思路
- 首先,要了解整除分块是干什么的?设计整除分块的题目大概都具有以下形式,也就是我们这道题的题目(最基本的形式)
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \cfrac{n}{i}\rfloor i=1∑n⌊in⌋ - 上面的括号代表向下取整,即整除
- 我们以 n = 6 n=6 n=6为例子,如下图
- 我们将 ⌊ n i ⌋ \lfloor \cfrac{n}{i} \rfloor ⌊in⌋都换成具体的正整数
- 我们可以发现对于一个 i i i,往往是一段区间的整除值都是同一个值(废话)
- 此处我们感性理解一下,比如从 4 , 5 , 6 4,5,6 4,5,6除 6 6 6的结果是 1.5 , 1.2 , 1 1.5,1.2,1 1.5,1.2,1
- 或看为,除法带余数,就出现一段区间他们的商都是一个值
- 现在,假如我们通常知道了区间内的某一个下标(一般是左端点),如何求得跟它所在区间的右端点?
- 设 n = i ∗ j + t , t ∈ [ 0 , i − 1 ] n=i*j+t,t\in [0,i-1] n=i∗j+t,t∈[0,i−1],显然我们可以这样表示 i i i,而把 j j j设置为倍数,显然 j j j也就是 ⌊ n i ⌋ \lfloor \cfrac{n}{i} \rfloor ⌊in⌋的值
- 对于右端点 r r r,必然有 t < j t<j t<j
- 反证法,若 t ≥ j t\geq j t≥j则,可以令 t ′ = t − j t'=t-j t′=t−j, n = r ∗ j + t = ( r + 1 ) ∗ j + t ′ n=r*j+t=(r+1)*j+t' n=r∗j+t=(r+1)∗j+t′
- 就得到了区间内比右端 r r r还靠右的满足条件的点
- 所以:对于右端点 r r r,必然有 t < j t<j t<j
- 我们就可以根据这个性质,通过 l l l,求得 j j j,再通过 j j j求得 r r r
- 故对于一个区间,我们知道左端点
l
l
l,即可以通过公式计算右端点
r
=
⌊
n
⌊
n
l
⌋
⌋
r = \lfloor \cfrac{n}{\lfloor \cfrac{n}{l}\rfloor}\rfloor
r=⌊⌊ln⌋n⌋,代码中写作
r = n / (n / i)
,注意此处为整除 - 既然我们知道了当前区间的左右端点,那么如何知道下一个区间的左端点呢?很显然,也就是右端点的下一个位置,即 l = r + 1 l = r +1 l=r+1
- 于是我们只要这样这样遍历下去,就可以求得
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \cfrac{n}{i}\rfloor i=1∑n⌊in⌋的值
代码实现
class Solution {
public:
int work(long long n) {
long long ans = 0;
for (long long l = 1, r; l <= n; l = r + 1) //整除分块
{
r = n / (n / l); //获取右端点
ans += (r - l + 1) * (n / l); //将当前区间的贡献加到答案上面
}
ans %= 998244353; //对答案进行取模
return ans;
}
};
复杂度分析
- 时间复杂度,为整除分块的时间复杂度,即 O ( n ) O(n) O(n),下面给出整除分块的时间复杂度证明:
- 对于 1 ≤ i ≤ n 1\leq i\leq \sqrt n 1≤i≤n,这样的 i i i最多有 n \sqrt n n个,考虑映射关系,一个 ⌊ n i ⌋ \lfloor \cfrac{n}{i}\rfloor ⌊in⌋与一个 i i i映射(实际上可能多个 i i i对应一个),最多也有 n \sqrt n n个
- 对于 n < i ≤ n \sqrt n < i \leq n n<i≤n,有 ⌊ n i ⌋ < n \lfloor \cfrac{n}{i}\rfloor<\sqrt n ⌊in⌋<n,这些 ⌊ n i ⌋ \lfloor \cfrac{n}{i}\rfloor ⌊in⌋都会落在 [ 1 , n ) [1,\sqrt n) [1,n)这个区间上面,最多就 n − 1 \sqrt n -1 n−1个
- 总共最多 2 n − 1 2\sqrt n -1 2n−1个,为 O ( n ) O(\sqrt n) O(n)级别,证毕
- 空间复杂度,定义了 a n s , l , r ans,l,r ans,l,r,为 O ( 1 ) O(1) O(1)