[BZOJ 1968] [AHOI 2005] 约数研究
Description
Input
只有一行一个整数 \(N\)。
Output
只有一行输出,为整数 \(M\),即 \(f(1)\) 到 \(f(N)\) 的累加和。
Sample Input
3
Sample Output
5
HINT
\(0 < N < 1000000\)
Solution
〖线性筛约数个数〗
设 \(d[i]\) 表示 \(i\) 的约数个数,\(num[i]\) 表示 \(i\) 的最小质因子的出现次数。
若 \(i=p_1^{k_1}p_2^{k_2}\cdots p_n^{k_n}\),有 \(d[i]=(k_1+1)(k_2+1)\cdots(k_n+1)\)。
- 若 \(i\) 是质数,则 \(d[i]=2,num[i]=1\)。
- 若 \(i \bmod p[j] \ne 0\),即 \(i\) 不包含质因子 \(p[j]\),那么 \(d[i\times p[j]]=(k_1+1)(k_2+1)\cdots(k_n+1)(1+1)=d[2]\times 2\),又因为是从小到大枚举,所以 \(p[j]\) 一定是 \(i\times p[j]\) 的最小质因子,那么 \(num[i\times p[j]]=1\)。
- 若 \(i \bmod p[j]=0\),则 \(p[j]\) 一定是 \(i\) 的最小质因子,那么 \(d[i\times p[j]]=(k_1+1+1)(k_2+1)\cdots(k_n+1)=d[i]/(num[i]+1)\times(num[i]+2),num[i\times p[j]]=num[i]+1\)。
void euler() {
for (int i = 2; i <= n; ++i) {
if (!np[i]) p[++tot] = i, d[i] = 2, num[i] = 1;
for (int j = 1; j <= tot && i * p[j] <= n; ++j) {
np[i * p[j]] = 1;
if (i % p[j] == 0) {
d[i * p[j]] = d[i] / (num[i] + 1) * (num[i] + 2);
num[i * p[j]] = num[i] + 1; break;
}
d[i * p[j]] = d[i] * 2, num[i * p[j]] = 1;
}
}
}
〖线性筛约数和〗
设 \(sd[i]\) 表示 \(i\) 的所有约数之和,则 \(sd[i]=(1+p_1+p_1^2+\cdots+p_1^{k_1})(1+p_2+p_2^2+\cdots+p_2^{k_2})\cdots(1+p_n+p_n^2+\cdots+p_n^{k_n})\)。
若 \(m\) 为 \(i\) 的最小质因子 \(p\) 出现的次数,设 \(sp[i]=1+p+p^2+\cdots+p^m\)。
- 若 \(i\) 是质数,则 \(sd[i]=sq[i]=i+1\)。
- 若 \(i\bmod p[j]\ne 0\),则 \(sd[i\times p[j]]=(1+p_1+p_1^2+\cdots+p_1^{k_1})(1+p_2+p_2^2+\cdots+p_2^{k_2})\cdots(1+p_n+p_n^2+\cdots+p_n^{k_n})(1+p[j])\)\(=sd[i]\times (p[j] + 1)\),而 \(p[j]\) 又是 \(i\times p[j]\) 的出现次数最小的质因子,所以 \(sp[i\times p[j]]=p[j]+1\)。
- 若 \(i\bmod p[j]=0\),则 \(p[j]\) 一定是 \(i\) 的出现次数最小的质因子,则 \(sd[i\times p[j]]=(1+p_1+p_1^2+\cdots+p_1^{k_1}+p_1^{k_1+1})(1+p_2+p_2^2+\cdots+p_2^{k_2})\cdots(1+p_n+p_n^2+\cdots+p_n^{k_n})\)\(=sd[i]/sp[i]\times (sp[i]\times p[j]+1),sp[i\times p[j]]=sp[i]\times p[j]+1\)。
void euler() {
for (int i = 2; i <= n; ++i) {
if (!np[i]) p[++tot] = i, sd[i] = sp[i] = i + 1;
for (int j = 1; j <= tot && i * p[j] <= n; ++j) {
np[i * p[j]] = 1;
if (i % p[j] == 0) {
sp[i * p[j]] = sp[i] * (sp[i] * p[j] + 1);
sd[i * p[j]] = sd[i] / sp[i] * sp[i * p[j]]; break;
}
sd[i * p[j]] = sd[i] * (p[j] + 1), sp[i * p[j]] = p[j] + 1;
}
}
}
此题除了用线性筛求解之外,还有一种更优秀的做法:\([1,n]\) 中 \(i\) 总共可以成为 \(\left\lfloor\dfrac{n}{i}\right\rfloor\) 个数的约数,即 \(ans=\sum\limits_{i=1}^n\left\lfloor\dfrac{n}{i}\right\rfloor\),可以用数论分块做到 \(O(\sqrt n)\)。
Code
#include <cstdio>
int main() {
int n, ans = 0; scanf("%d", &n);
for (int i = 1, j; i <= n; i = j + 1)
j = n / (n / i), ans += (j - i + 1) * (n / i);
printf("%d\n", ans);
return 0;
}