2021 SDSC D1 基础数论
Part 1 初月(Easy Mode)
质数
暴力
从 \(2\) 枚举到 \(⌊\sqrt n⌋\) 试试是否可以整除就可以啦。
显然时间复杂度是 \(O(\sqrt n)\) 的。
埃氏筛
刚才的试除法让我们知道,找到一个数的因数是很难的。
但是找到一个数的倍数就很简单了。
因此我们可以从 \([2, n]\) 中依次枚举,每个数的倍数必然不是质数
复杂度?
\(F(n)=\frac{n}{2}+\frac{n}{3}+⋯+\frac{n}{n}=O(n \log n)\)。
实际上底数是 \(e\),因此调和级数的常数很小,\(1s\) 跑得过。
由算术基本定理可知,只需要枚举质数的倍数即可。
枚举 \(p\) 的倍数时,只需从 \(p^2\) 开始枚举。
时间复杂度 \(O(n \log \log n)\)。
线性筛
刚才的做法已经很优秀了,可是为什么还不是线性呢?
原因仍然在于,一个数字可能被筛掉多次。
例如 \(n=15\),那么 \(12\) 这个数会在 \(p=2\) 和 \(p=3\) 时各被筛一次。
想要做到线性,就必须让一个数只被一个质数筛掉,我们选择这个数为它最小的质因数。
首先从 \(2\) 到 \(n\) 枚举自然数 \(q\)(不一定是质数),再从小到大枚举比 \(q\) 小的质数 \(p\),筛掉 \(pq\);如果 \(q\) 是 \(p\) 的倍数,就跳出内层循环。
void primes(int n)
{
memset(v, 0, sizeof(v)); //清空标记数组
m = 0; //质数个数
for (int i = 2; i <= n; i++)
{
if (!v[i]) //未被标记,i为质数
v[i] = i, prime[++m] = i; //记录
for (int j = 1; j <= m; j++)
{
if (prime[j] > v[i] || prime[j] > n / i)
break; //i有更小的质因子,或者超出n的范围
v[i * prime[j]] = prime[j]; //prime[j]为合数 i*prime[j]的最小质因子
}
}
}
根据“高斯素数定理”,如果需要粗略的估计 \([2, n]\) 内的素数个数,可以直接硬点它为 \(\frac{n}{\ln n}\)。
GCD & LCM
GCD,Greatest Common Divisor 的缩写,意为最大公约数
LCM,Least Common Multiple 的缩写,意为最小公倍数
\(\gcd(a, b)=\gcd(a−b, b)= \gcd(b, a\text{ mod } b)\)
注意到每次取模至少减半,因此复杂度为 \(O(\log n)\)
而求 LCM 只需求出 GCD ,因为:
\(\gcd(a, b) \times \text{lcm}(a, b)=a \times b\)。
进制转换
将 \(n\) 位 \(a\) 进制数转化为 \(b\) 进制数,最终为 \(m\) 位。
将给定的 \(a\) 进制数从高位向低位扫描,每次将当前结果乘以 \(a\),并加上当前位的系数,再从低位向高位进位即可。
复杂度 \(O(nm)\),用多项式科技可以优化,但大多数情况下没必要。
瓶颈在于 \(n\) 次乘 \(a\) 和进位,每次都是 \(O(m)\) 的。
如果 \(a, b\) 比较小,可以通过压位的方式进一步优化。
即,先将原数转化为 \(a^p\) 进制,再转化为 \(b^q\) 进制,可以将复杂度降为 \(O(\frac{nm}{pq})\),为了计算方便,以 \(a^p, b^q\) 不超过 \(2×10^9\) 为妙。
Part 2 三日月(Normal Mode)
同余
若对于给定的正整数 \(m\),有正整数 \(a, b\),满足 \(a=km+b, k∈Z\),则称 \(a, b\) 模 \(m\) 同余,用 \(a≡b (\mod m)\) 表示。
模 \(m\) 意义下,整数集只剩下了 \([0, m)\) 的 \(m\) 个整数,其他整数都应该加上或减去若干个 \(m\) 来调整到这个区间内。
注意到负整数 \(x\) 对应的数是 \(km+x\),而不是 \(km−x\)。
顺便提一句,\(a \mod b=a−⌊\frac ab⌋ \times b\),这一点后面还会提到。
不难证明,模意义下的加法、减法、乘法与一般意义下相同,各运算规律依然满足。
但是很可惜,除法与它们不同。
考虑一般意义下的除法,\(a÷b\) 等价于 \(a \cdot b^{-1}\),而 \(b^{−1}\) 满足 \(b\cdot b^{−1}=1\) 这一性质,从而进行乘法的逆运算。
换句话说,假如我们能对任意 \(b\) 找到 \(b^{−1}\),使得\(b\cdot b^{−1}≡1(\mod m)\),不就能实现模 \(m\) 意义下的除法了吗?
逆元
因此,我们需要引入逆元的概念。
大多数情况下,当我们需要在模意义下做除法时,模数都是质数。
此时 \((0, m)\) 中每个整数都有唯一的逆元。
inv[0] = inv[1] = 1;
for (register int i(2); i <= n; i++)
inv[i] = (1ll * (mod - mod / i) * inv[mod % i]) % mod;
费马小定理
若整数 \(a∈(0, m)\) 且 \(m\) 为质数,则 \(a^{m−1}≡1 (\mod m)\)。
有了它,我们就能求出 \(a\) 的逆元了:
\(a\cdot a^{m−2}=a^{m−1}≡1 (\mod m)\),所以 \(a^{−1}≡a^{m−2} (\mod m)\)。
绝大多数情况下逆元都是这样求的。
扩展欧几里得算法 & 裴蜀定理
设 \(x=a^{−1}\),那么有 \(a\cdot x≡1 (\mod m)\)。
将它写成一般形式,得到 \(x, y\) 的不定方程 \(ax−my=1\)。
如果能得到一组整数解 \((x_0, y_0)\),我们就可以得到它的通解:
\(x=x_0+km, y=y_0+ka, k∈Z\),选择合适的 \(k\) 即可得到 \(x\)。
现在问题在于,方程是否有解,以及如何找到一组特解。
必要性很好证明,而下面要讲的扩展欧几里得算法则通过构造证明了充分性。
设 \(d=\gcd(a, b)\),扩展欧几里得算法可以给出不定方程 \(ax+by=d\) 的一组整数特解 \((x_0, y_0)\)。
类比刚才的情况,通解有 \(x=x_0+k\cdot \frac bd, y=y_0−k\cdot \frac ad, k∈Z\)。
对于 \(ax+by=c, d|c\) 的情况,可以先解 \(ax+by=d\),再将 \(x, y\) 都乘以 \(\frac cd\)。
考虑之前欧几里得算法求 \(\gcd\) 的过程,我们从 \(\gcd(a, b)\) 递归到 \(\gcd(b, a \mod b)\)。
设 \(a^′=b, b^′=a \mod b\),那么如果我们有了不定方程 \(a^′x+b^′y=d\) 的一组整数解 \((x^′, y^′)\),能否推出 \(ax+by=d\) 的一组整数解 \((x, y)\) 呢?
\(b^′=a \mod b=a−⌊a/b⌋ \cdot b\),代回 \(a^′x^′+b^′y^′=d\) 得到 \(bx^′+(a−⌊a/b⌋\cdot b)y^′=d\),整理得$ ay′+b(x′−⌊a/b⌋\cdot y^′)=d$。
也就是说,令 \(x=y^′, y=x^′−⌊a/b⌋\cdot y^′\) 即可完成递归的回推
裴蜀定理:对于给定的正整数 \(a, b\),设 \(d=\gcd(a, b)\),则关于 \(x, y\) 的不定方程 \(ax+by=c\) 存在整数解,当且仅当 \(d|c\)。
欧拉函数
欧拉函数:定义域为正整数的函数 \(φ(x)\),表示 \([1, x]\) 中与 \(x\) 互质的正整数个数。
设 \(x=∏p_i^{e_i}\),则 \(φ(x)=x\cdot ∏\frac{p_i−1}{p_i}\),证明可以考虑每次划掉 \(x\) 所有质因子的倍数,剩下的就是与 \(x\) 互质的。
欧拉函数可以线性筛,只需判断当前质因子是否出现过,以选择乘以 \(p\) 还是 \(p−1\)。
int prime[N], phi[N];
bool isprime[N];
void pre()
{
ll cnt = 0;
isprime[1] = 1;
phi[1] = 1;
for (register int i(2); i <= n; ++i)
{
if (!isprime[i])
{
prime[++cnt] = i;
phi[i] = i - 1;
}
for (register int j(1); j <= cnt and i * prime[j] <= n; ++j)
{
isprime[i * prime[j]] = 1;
if (i % prime[j])
phi[i * prime[j]] = phi[i] * phi[prime[j]];
else
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
}
欧拉定理
那么为什么要讲欧拉函数呢?就是为了引出欧拉定理!
设 \(a, m\) 互质,则 \(a^{φ(m)}≡1 (\mod m)\)。
将 \([1, m]\) 中与 \(m\) 互质的数列出来,设为 \(b_1, b_2, ⋯, b_{φ(m)}\)。
设 \(c_i=a\cdot b_i \text{mod }m\),可以证明 \(c_i\) 两两模 \(m\) 不同余,且任意 \(c_i\) 与 \(m\) 互质。
那么 \(c\) 其实是 \(b\) 重排的结果,因为与 \(m\) 互质的数只有 \(φ(m)\) 个。
因此 \(∏ b_i≡∏ c_i≡a^{φ(m)}\cdot ∏ b_i (\mod m)\),得证。
前面提到的费马小定理其实是欧拉定理的子定理。
扩展欧拉定理
刚才的欧拉定理只适用于 \(a, m\) 互质的情况
对于不互质的情况,此处不证明地给出扩展欧拉定理:
若 \(c<φ(m),则 a^c≡a^c (\mod m)\)
若 \(c≥φ(m),则 a^c≡a^{[c \mod φ(m)]+φ(m)} (\mod m)\)
证明比较复杂,有兴趣的同学可以自行查阅或浏览这篇博客。