数论分块
如何在\(O(\sqrt{n})\)复杂度内计算 \(\sum \limits_{i= 1}^n \lfloor \frac{n}{i} \rfloor\) ?
一个直观的想法是, \(\lfloor \frac{i}{n} \rfloor\) 必然存在一个相等的段,我们以\(n = 15\) 为例:
\[\lfloor \frac{n}{i} \rfloor (n=15) \\
\]
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
15 | 7 | 5 | 3 | 3 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
我们可以看到该段区间内当 \(n\) 取值为 \([6, 8], [9, 15]\)内的值都是相等的值,我们考虑如何以每个这样的块为单位来计算要求的值。
考虑左端点为 \(l\), 我们设右端点为 \(r\), 有如下等式
\[\because \lfloor \frac{n}{r} \rfloor \le \frac{n}{r} \\
\therefore r \le \frac{n}{\lfloor \frac{n}{r} \rfloor} \\
又\because \lfloor \frac{n}{l} \rfloor = \lfloor \frac{n}{r} \rfloor, 且 r取值为整数 \\
\therefore r_{max} = \frac{n}{\lfloor \frac{n}{l} \rfloor}
\]
例题
#include <iostream>
using i64 = long long;
int main() {
i64 n, k; std::cin >> n >> k;
i64 sum = 0;
i64 l = 1, r = 0;
while (l <= k && l <= n) {
r = std::min(k / (k / l), n);
i64 t = r - l + 1;
sum += t * k - (l + r) * t / 2 * (k / l);
l = r + 1;
}
std::cout << sum + std::max(0LL, 1LL * (n - k) * k);
}