散落星河的记忆🌠
Published on 2017-09-02 11:31 in 暂未分类 with 散落星河的记忆🌠

[BZOJ 1968] [AHOI 2005] 约数研究

Description

img

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;
}
posted @ 2019-02-13 21:44  散落星河的记忆🌠  阅读(191)  评论(0编辑  收藏  举报