P1403 [AHOI2005] 约数研究
题目原意是设\(f(i)=d(i)\),即表示自然数\(i\)的约数个数,题目给定输入\(n\),需要我们计算出\(\sum_{i=1}^nf(i)\)进行返回。
解析
解法一
数论函数
结合线性筛会利用合数的最小质因数筛去合数,并且约数个数的函数属于积性函数。
#include <iostream>
#include <vector>
int main() {
int n;
std::cin >> n;
std::vector<int> primes;
std::vector<bool> isPrime(n + 1, true);
std::vector<int> d(n + 1, 0);
std::vector<int> num(n + 1, 0);
isPrime[0] = isPrime[1] = false;
d[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
num[i] = 1;
d[i] = 2;
}
for (int pj : primes) {
if (i * pj > n) break;
isPrime[i * pj] = false;
if (i % pj == 0) {
num[i * pj] = num[i] + 1;
d[i * pj] = d[i] / (num[i] + 1) * (num[i * pj] + 1);
break;
} else {
num[i * pj] = 1;
d[i * pj] = d[i] * 2;
}
}
}
int ans = 0;
for (int di : d) ans += di;
std::cout << ans << std::endl;
return 0;
}
解法二
\(f(n)\)表示正整数\(n\)的因数个数,也可以理解为枚举\(n\)的因数\(d\),每枚举一个因数\(d\),\(f(n)\)加\(1\)。两种数学解释对应着两种不同的表达式,即\(f(n)=\sum_{d|n}1\),或\(f(n)=\sum_{d=1}^n[d|n]\)。后者的语义是\([d|n]\)是一个判断正误的函数,正确为\(1\),错误为\(0\)。
题目要求我们求解\(I=\sum_{i=1}^nf(i)\),将\(f(i)\)的定义式代入:
求和符号可以交换顺序,把\(d\)的枚举提前:\(I=\sum_{d=1}^n\sum_{i=1}^n[d|i]\)
相当于分别考虑\(d=1\)时,对\(i=1,2,3,\cdots,n\)的贡献;\(d=2\)时,对\(i=1,2,3,\cdots,n\)的贡献;\(\cdots\);\(d=n\)时,对\(i=1,2,3,\cdots,n\)的贡献。
对于一个固定的\(d\),\(\sum_{i=1}^n[d|n]\)的意义就是说明在\(1\sim n\)中,有多少个数字有\(d\)这个约数,即有多少个数是\(d\)的倍数,\(\sum_{i=1}^n[d|n]=\lfloor {n\over d}\rfloor\)。
结合\((1)\),我们得到了\(I=\sum_{d=1}^n\lfloor{n\over d}\rfloor\)的结论。这种思路叫做“算两次”或“调换求和顺序”,在数论问题(特别是结合莫比乌斯反演时)的求解中是很不错的想法。
#include <iostream>
int main() {
int n;
std::cin >> n;
int ans = 0;
for (int i = 1; i <= n ;i++) {
ans += n / i;
}
std::cout << ans << std::endl;
return 0;
}

P1403 [AHOI2005] 约数研究
浙公网安备 33010602011771号