交换约数统计顺序得到 sum floor(n/d),再按相同商的连续区间整段求和。
OJ: luogu
题目 ID: P1403
难度:普及+/提高
标签:整除分块约数数论python
日期: 2026-07-16 19:20
题意
求 1..n 每个整数约数个数的总和。
思路
约数 d 会成为 d,2d,... 共 floor(n/d) 个数的约数,因此交换统计顺序:
还可以利用整除分块。对于当前 left,商 q=n//left 在连续区间 [left,n//q] 内不变,整段贡献为 q*(right-left+1),然后跳到下一段。
Python 知识
//是整数向下除法,直接对应公式中的下取整。divmod不需要用于这里,因为商相同区间右端可由n//quotient得到。- Python 大整数安全累加约数总数。
while left<=n按块跳跃,循环次数约为2*sqrt(n)。/home/rainboy/mycode/hugo-blog/content/program_language/python/cpp_to_python_pitfalls.md:整数除法和任意精度。/home/rainboy/mycode/hugo-blog/content/program_language/python/generator_expression.md:从逐项求和到按块归并。
代码
python
n = int(input())
answer = 0
left = 1
while left <= n:
quotient = n // left
right = n // quotient
answer += quotient * (right - left + 1)
left = right + 1
print(answer)cpp
/**
* P1403 [AHOI2005] 约数研究
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-27 00:00
* update_at: 2026-07-27 00:00
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
long long ans = 0;
// 1~n 中,约数 d 出现 ⌊n/d⌋ 次
// 对 ⌊n/d⌋ 相等的 d 合并计算
for (int l = 1, r; l <= n; l = r + 1) {
int q = n / l;
r = n / q;
ans += 1LL * q * (r - l + 1);
}
printf("%lld\n", ans);
return 0;
}复杂度
整除分块时间复杂度
总结
从“每个数有多少约数”切换为“每个约数出现多少次”得到调和和,再按相同商分块可继续加速。