定义
设有整数 \(a\) 和 \(b\) ,如果存在整数 \(q\) 使得 \(a = b \times q\) ,则称 \(b\) 是 \(a\) 的约数, \(a\) 是 \(b\) 的倍数,记为 \(b\ |\ a\) 。
算数基本定理的推论
在算数基本定理中,任何一个大于 \(1\) 的正整数 \(N\) 都能唯一分解为有限个质数的乘积,可写作:
其中:\(c_i \in \mathbb{N}_+\) , \(p_i\) 都是质数,并且满足 \(p_1 < p_2 < \cdots<p_m\) 。
则有以下推论:
\(N\) 的正约数集合为:
\(N\) 的正约数个数为:
\(N\) 的所有正约数和为:
求 \(N\) 的正约数合集
试除法
如果 \(n\) 有约数 \(a\) 和 \(b\) ,且 \(a\times b=n\) 。如果 \(a\le\sqrt n\) ,则 \(b\ge\sqrt n\) ,反之亦然。换句话说,约束是成对出现的(除了完全平方数 \(\sqrt n\) 单独出现)。
所以,只需要扫描 \(1\sim\sqrt n\) 的每个整数 \(x\) ,判断 \(x\) 是否能整除 \(n\) ,若能,则 \(x\) 与 \(\large\frac{n}{x}\) 都是 \(n\) 的约数。时间复杂度为 \(\mathcal{O}(\sqrt n)\) 。
试除法的推论
由上述的扫描过程不难发现一个整数 \(n\) 的约数个数至多有 \(2\sqrt n\) 个。
代码:
int cnt;
int num[N];for (int i = 1; i * i <= n; i++) {if (!(n % i)) {num[++cnt] = i;if (i != n / i) num[++cnt] = n / i;}
}
倍数法
不难发现,试除法在查找单个数的约数集合效率较高,但是不适用于批量查询,我们考虑优化。
在 \(\text{Eratosthenes}\) 筛法过程中我们可发现大量数字中一般会有共同的约数,此时进行试除法就会重复判断,所以我们利用正难则反的逆向思维选择用数字 \(x\) 本身去标记它的倍数 \(x, 2x, \cdots,\large{\frac{n}{x}}x\) 。
时间复杂度为 \(\mathcal{O}\left(n + \large{\frac{n}{2}}+\large{\frac{n}{3}}+\cdots+\large{\frac{n}{n}}\right)=\mathcal{O}\left(\sum\limits_{i=1}^n\large\frac{n}{i}\right) = \mathcal{O}(n \log n)\) 。
vector<int> num[N];for (int i = 1; i <= n; i++) for (int j = 1; j * i <= n; j++) num[i * j].push_back(i);
倍数法的推论
由此可见, \(1\sim N\) 中每个数的约数个数总和大约为 \(n \log n\) 。
调和级数
在上述时间复杂度中出现了一个很奇怪的式子,就是:\(\large{\sum\limits_{i=1}^n\frac{n}{i}} = n\sum\limits_{i=1} ^n\frac{1}{i}\) 。要想求解它,我们需要了解一下调和级数
定义
性质
- 发散性,即:
- 渐近性,即:
- 其中