整除分块(算法证明+复杂度证明)

题目陈述

  • 大意:求解下面的表达式的值

∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \cfrac{n}{i}\rfloor i=1nin

算法一:朴素算法

算法思路

  • 暴力算法,枚举每个 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=1nin
  • 上面的括号代表向下取整,即整除
  • 我们以 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=ij+tt[0,i1],显然我们可以这样表示 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 tj则,可以令 t ′ = t − j t'=t-j t=tj, n = r ∗ j + t = ( r + 1 ) ∗ j + t ′ n=r*j+t=(r+1)*j+t' n=rj+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=lnn,代码中写作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=1nin的值

代码实现

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 1in ,这样的 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 <in,有 ⌊ 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)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值