目标是求的和:
做法是将相同\lfloor \frac{n}{i}\rfloor的连续一个区间看做一个块,例如区间中所有的\lfloor \frac{n}{i}\rfloor的值都相同,那么我直接
所以依次枚举区间的左边界,然后找到最大的右边界,然后累加,最后将设置为,继续找下一个块
下面是基本代码:
int ans = 0;
for(int i = 1; i <= n; i ++){
    int l = i, r = 😀
    ans = ans + (r - l + 1) * (n / i);
    i = r;
}
cout << ans << endl;
有两个问题:
  1. 最大可以为,这样枚举一个个块,虽然比直接的暴力循环优化了许多,但是能保证仍然不会超时吗?
  2. 最关键的,如果求出有边界
回答问题1:
显然时间复杂度直接与枚举的块的数量直接挂钩,如果块有,那依旧超时。但是,块的数量不会超过
证明:
将区间拆分为两个区间
区间只有个数,那么块的数量肯定就是小于等于
区间的数可能很多,例如当时,,那么有个数,但是,它的商值的个数不会超过个,也就是说块的数量也不会超过个个
所以区间的块的数量不会超过2\sqrt{n}

回答问题2
如何求有边界?给出结论r=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor,那么下面需要证明两个等式:


  1. \lfloor \frac{n}{l} \rfloor > \lfloor \frac{n}{r + 1} \rfloor
为什么要证明等式1?因为在同一个块,那么它们的商值就一定相同
证明过程:
先证明:
,那么两边取倒数得到,然后两边同时乘以,得到
因为r=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor,因为为整数,那么r=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor,所以
得到,即

在证明\lfloor \frac{n}{l} \rfloor \leq\lfloor \frac{n}{r} \rfloor
因为r=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor,两边同时取倒数,得到, 两边同时乘以,得到
然后 两边在同时取整数,得到 ,因为已经时整数了,所以直接是,即
那么得到证明

那为什么要证明等式2?因为这个有区间边界肯定要最大,要不然这个块就没有取完整,例如是一个块,但是取的,那肯定就不对
首先令,即证明\lfloor \frac{n}{l} \rfloor > \lfloor \frac{n}{r + 1} \rfloor,其中
,即,那么\frac{n}{r + 1} < k
所以\lfloor \frac{n}{l} \rfloor > \lfloor \frac{n}{r + 1} \rfloor,因为是一个整数,那么\lfloor \frac{n}{l} \rfloor > \lfloor \frac{n}{r + 1} \rfloor
所以\lfloor \frac{n}{l} \rfloor > \lfloor \frac{n}{r + 1} \rfloor得到证明


总代码:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;


signed main(){
    HelloWorld;
    
    int n; cin >> n;
    int ans = 0;
    for(int i = 1; i <= n; i ++){
        int l = i, r = (n / (n / i));
        ans = ans + (r - l + 1) * (n / i);
        i = r;
    }
    cout << ans << endl;
    return 0;
}