如果你以前从未接触过数论,AoPS 是一个很好的起点。
| Resources | |||||
|---|---|---|---|---|---|
| AoPS | 练习题;请将训练主题设置为数论 | ||||
| AoPS | 一本不错的入门书 | ||||
学习资料
| Resources | |||||
|---|---|---|---|---|---|
| IUSACO | 本模块以此为基础 | ||||
| David Altizio | |||||
| CPH | |||||
| PAPS1 | |||||
| MONT | |||||
| AoPS | 包含优秀的证明和习题 | ||||
质因数分解
若非负整数 能被正整数 整除,即存在某个整数 使得 , 则称 是 的因数(或约数)。若整数 的正因数只有 和 ,则称 为质数。大于 且不是质数的整数称为合数。
每个正整数都有唯一的质因数分解,即可以按照下式唯一地写成若干质数的乘积:
其中 是互不相同的质数, 是正整数。
下面讨论如何求任意正整数的质因数分解。
from typing import Listdef factor(n: int) -> List[int]:ret = []i = 2while i * i <= n:while n % i == 0:ret.append(i)n //= ii += 1if n > 1:ret.append(n)return ret
该算法的时间复杂度为 ,因为 for 循环最多检查
个候选因数。虽然其中还有一个 while 循环,但每次用 除去
都会迅速减小 ,从而让外层循环更早结束,并不会增加渐进复杂度。
以 为例,算法执行过程如下:
此时 for 循环结束,因为 已经大于 。最后把
加入因数列表,否则它不会在循环中被加入。最终得到质因数分解
。
Focus Problem – try your best to solve this problem before continuing!
题解——Counting Divisors
最直接的做法就是按题意计算:对每个 ,在 时间内 求出它的因数个数。
由于 Python 的常数较大,下面的代码会在不少测试点上超时。
ans = []for _ in range(int(input())):div_num = 0x = int(input())i = 1while i * i <= x:if x % i == 0:div_num += 1 if i**2 == x else 2i += 1ans.append(div_num)print("\n".join(str(i) for i in ans))
这个解法的时间复杂度为 ,刚好足以通过。不过,我们 还可以把它优化到 。
首先来看质因数分解的一个重要性质。设:
那么 的因数个数就是 。
这是因为在 的任意因数中, 的指数只能取 中的整数; 不同的指数选择会产生不同的因数,因此每个 都为乘积贡献 种选择。
最多有 个不同质因数。因此,只要能高效求出 的质因数分解,就可以利用上述性质在 时间内回答一次 询问,而不再需要 。
下面介绍如何用 的预处理,使每次质因数分解只需 时间:
- 不断用预处理出的质因数去除 ,直到 ,即可得到完整的质因数分解。
这一方法的实现如下:
MAX_N = 10**6# max_div[i] contains the largest prime that can go into imax_div = [0 for _ in range(MAX_N + 1)]for i in range(2, MAX_N + 1):if max_div[i] == 0:for j in range(i, MAX_N + 1, i):max_div[j] = ians = []
最大公因数与最小公倍数
最大公因数
两个整数 的最大公因数(GCD),是同时作为二者因数的最大整数。 求两个非负整数的最大公因数时,可以使用欧几里得算法:
该算法可以递归实现如下:
def gcd(a: int, b: int) -> int:return a if b == 0 else gcd(b, a % b)
比赛中通常无需自己实现,因为内置的 math 库提供了 gcd 和 lcm 函数。
该函数的复杂度为 ,因为 。
欧几里得算法的最坏情况发生在 是相邻斐波那契数 时。 此时算法会依次计算 . ,总共需要 次调用,与 成正比。
最小公倍数
两个整数 的**最小公倍数(LCM)**是能同时被二者整除的最小正整数。 它可以利用最大公因数计算:
若直接写成 a * b / gcd(a, b),当 a * b 超出数据类型范围时可能发生整数
溢出(例如 C++ 和 Java 的 int 上限约为 20 亿)。应先计算
a / gcd(a, b),再乘以 b;只要最终结果可表示,这样就能避免中间溢出。
这两种运算都满足结合律。因此计算多个数的 GCD 或 LCM 时,可以按任意顺序 每次合并两个数。例如:
欧拉函数
Focus Problem – try your best to solve this problem before continuing!
| Resources | |||||
|---|---|---|---|---|---|
| cp-algo | 理论与习题 | ||||
| CF | 讲解全面的文章 | ||||
性质
欧拉函数记作 ,表示区间 中与 互质的正整数个数。 若 ,则称 互质。
前 20 个正整数的 如下:
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 2 | 4 | 2 | 6 | 4 | 6 | 4 | 10 | 4 | 12 | 6 | 8 | 8 | 16 | 6 | 18 | 8 |
欧拉函数是积性函数:当 时, 。例如 。
来看 的几个特殊情况:
- 若 是质数,则 ,因为所有 都满足 ;
- 若 是质数幂,其中 为质数且 ,则恰有 个数 能被 整除,因此
利用积性和上述质数幂公式,可以由 的质因数分解计算 。设 ,其中 是 的质因数,则:
下面的实现在 时间内分解质因数。ans -= ans / p
可能不太直观:设 ,其中 是质因数, 是其余部分。减去
后得到
,恰好对应上面的欧拉函数公式。
def phi(n: int) -> int:ans = np = 2while p * p <= n:if n % p == 0:while n % p == 0:n //= pans -= ans // pp += 1if n > 1:ans -= ans // nreturn ans
题目中经常需要预处理 到 的所有欧拉函数值,此时逐个分解并不高效。 可以采用与埃氏筛相同的思想,时间复杂度为 。
def precompute():for i in range(1, MAX_N):phi[i] = ifor i in range(2, MAX_N):# If i is primeif phi[i] == i:for j in range(i, MAX_N, i):phi[j] -= phi[j] // i
Focus Problem – try your best to solve this problem before continuing!
题解
题目要求计算所有满足 的数对 的 GCD 之和:
定义辅助函数 ,表示第二个元素固定为 时的 GCD 之和:
该函数对所有 的 求和。当 时,必有 且 。满足条件的 的个数为 。因此可以把 改写为对 的因数求和:
我们可以高效计算所有 的 。不必逐个分解质因数,只需枚举 每个可能的因数 ,再更新它的所有倍数 。对固定的 ,向每个倍数 的 中加入贡献 。
最后,题目只统计 。而 包含 的情况(此时 ),必须将其减去。因此答案是调整后数值的前缀和:
该预处理的总复杂度由调和级数界定:
#include <bits/stdc++.h>using namespace std;using ll = long long;const int MAX_N = 1e6;ll phi[MAX_N + 1];ll f[MAX_N + 1];ll sum[MAX_N + 1];int main() {
练习题
| Status | Source | Problem Name | Difficulty | Tags | ||
|---|---|---|---|---|---|---|
| AC | Easy | Show TagsPrime Factorization | ||||
| CF | Easy | Show TagsDivisibility, Modular Arithmetic | ||||
| CF | Easy | Show TagsNumber Theory | ||||
| CF | Easy | Show TagsDivisibility | ||||
| CSES | Easy | Show TagsFunctional Graph, Prime Factorization | ||||
| SPOJ | Easy | Show TagsDivisibility | ||||
| CSES | Medium | Show TagsDivisibility | ||||
| CF | Medium | Show TagsPrime Factorization | ||||
| CC | Medium | Show TagsDivisibility | ||||
| CSES | Hard | Show TagsDivisibility | ||||
| SPOJ | Hard | Show TagsDivisibility, Euler Totient | ||||
| CF | Hard | Show TagsDivisibility | ||||
| AC | Hard | Show TagsDivisibility | ||||