数论学习笔记
主要参考 OI-Note Chapter4.1 整除与同余 - 知乎
整除
若 \(a / b (b \ne 0)\) 为整数,则称 \(b\) 整除 \(a\) ,记作 \(b \mid a\) 。若 \(a / b\) 和 \(c / b\) 的余数相等,则称 \(a, c\) 模 \(b\) 同余。
同余
关于同余,有以下命题等价:
- \(a\) 和 \(b\) 是模 \(d\) 同余的。
- 存在某个整数 \(n\) ,使 \(a = b + nd\) 。
- \(d\) 整除 \(a - b\) 。
由此可以轻易推出以下性质:
同余的基本性质
-
\[a \equiv b \pmod p \text{且} c \equiv d \pmod p \Rightarrow a \pm c \equiv b \pm d \]
-
\[a \equiv b \pmod p \Rightarrow ak \equiv bk \pmod {pk} \]
-
\[k\mid p \text{且} a \equiv b \pmod p \Rightarrow a \equiv b \pmod k \]
模运算基本性质
-
\[(a + b) \bmod p = ((a \bmod p) + (b \bmod p)) \bmod p \]
-
\[ab \bmod p = (a \bmod p)(b \bmod p) \bmod p \]
-
\[a \bmod kb \bmod b = a \bmod b \]
欧几里得辗转相除法(gcd)
给定整数 \(a, b\) ,将它们的最大公因数记作 \(\gcd(a, b)\) ,欧几里得辗转相除法便是用来求 \(\gcd(a, b)\) 的,因而简称 \(\gcd\) 。
记 \(a, b\) 的公因数集合为 \(T\) ,\(b , a \bmod b\) 的公因数集合为 \(T'\) ,有 \(T = T'\) 。证明:
设 \(u \subseteq T\) ,\(a = xu, b = yu\) ,$$a \bmod b = a - b \left \lfloor a / b \right \rfloor = xu - yu(\left \lfloor a / b \right \rfloor) = u[x - y(\left \lfloor a / b \right \rfloor)]$$ ,所以 \(u \mid a \bmod b\) , \(T \subseteq T'\) 。
同样的,记 \(v \subseteq T'\) ,\(b = nv, a \bmod b = mv\) ,$$a = a \bmod b + b \left \lfloor a / b \right \rfloor = mv + nv(\left \lfloor a / b \right \rfloor) = v[m + n\left \lfloor a / b \right \rfloor]$$ ,所以 \(v \mid a\) , \(T' \subseteq T\)。
综上, \(\gcd(a, b) = \gcd(b, a \bmod b)\) ,因而可以直接递归求解。时间复杂度为 \(\log(n)\) 。
边界:当 \(b = 0\) 时,\(\gcd(a, b) = a\) 。
code:
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
扩展欧几里得(exgcd)
由欧几里得辗转相除法,我们可以运用这个算法在求最大公因数的同时求出满足 $$ax + by = \gcd(a, b)$$ 的正整数解 \(x, y\) 。
对于边界条件 \(b = 0\) , \(x = 1, y = 0\) 就是一组合法解(当然,此处 \(y\) 可以取任意数)。那么,设 \(a' = b, b' = a \bmod b\) ,且我们已经求出 \(x_0, y_0\) 满足 \(a'x_0 + b'y_0 = \gcd(a', b')\) ,于是:
因此,满足 \(ax + by = \gcd(a, b)\) 的 \(x = y_0, y = x_0 - \left \lfloor a / b \right \rfloor y_0\) 。
code:
int exgcd(int a, int b, int &x, int &y){
if(!b){
x = 1, y = 0;
return a;
}
int a1 = exgcd(b, a % b, x, y);
int x1 = y, y1 = x - a / b * y;
x = x1, y = y1;
return a1;
}
乘法逆元
对于一个数 \(a\) ,若 \(ab = 1\) ,那么我们称 \(a\) 的逆元为 \(b\) ,记作 \(a^{-1}\) 。在同余中,则是若 \(ab \equiv 1\) , 则 \(b\) 为 \(a\) 的逆元。通常,我们通过找到 \(a\) 的逆元来避免除法。给定 \(a\) 和 \(b\) ,我们需要求出 \(x\) 满足 \(ax \equiv 1 \pmod b\) 。
逆元唯一性定理:逆元若存在,则总是唯一的。
反证法。设有 \(x \not \equiv y\) 使得 \(ax \equiv ay \equiv 1\) ,则有 \(axy \equiv ax(y) \equiv y\) ,且 \(axy \equiv ay(x) \equiv x\) , 则 \(x \equiv y\) ,矛盾。
逆元存在性定理:在模 \(m\) 下,当且仅当 \(a \bot m\) 时,\(a\) 的逆元存在。后续补充证明。
事实上, \(ax \equiv 1 \pmod b\) 等价于 \(ax + by = \gcd(a, b)\) 。由逆元存在性定理,\(a \bot b\) 时, \(ax + by = 1\) 。所以 \(ax = 1 - by\) , \(ax \equiv 1\) 。而若 \(ax \equiv 1 \pmod b\) ,则 \(ax - 1 \equiv 0 \pmod b\) ,所以令 \(y = \frac{ax - 1}{b}\) , 就有 \(ax + by = 1\) 。因此这两者等价。我们就可以直接使用exgcd求出 \(a\) 在模 \(b\) 下的乘法逆元。不过有一个需要注意的是,使用exgcd求出来的逆元可能是负数,输出时加一个 (x % p + p) % p 就好啦。
线性求逆元
我们知道 \(a \times a^{-1} \equiv 1\) 。
设 \(p = n \times i + r\) ,就有:
同时乘以 \(i^{-1}, r^{-1}\) 得:
移项得:
即
又因为 \(p \bmod i\) 比 \(i\) 小,我们就可以 \(O(n)\) 求逆元了!
裴蜀定理
不定方程 \(ax + by = c\) 有整数解 \(x, y\) 的充要条件是 \(\gcd(a, b) \mid c\) 。
证明:
当 \(\gcd(a, b) \mid c\) 时,我们可以先用exgcd求出\(ax +by = \gcd(a, b)\) 的解,然后同时乘以 \(\ c / gcd(a, b)\) 即可得出解。
当 \(\gcd(a, b) \not \mid c\) 时,在该方程两边同时除以 \(\gcd(a, b)\) ,发现方程左边为整数,右边为分数,则必定无解。
由此,我们就可以运用裴蜀定理直接证明逆元存在性定理了。
另外, 若 \(c \bot m, ac \equiv bc \pmod m\) ,运用逆元可以得出 \(a \equiv b\) 。这也是一条同余的性质。
虽然是个板子题,但是非常恶心。
无解情况裴蜀定理判定即可。
对于有解:
首先我们就可以通过exgcd求出满足 \(ax + by = c\) 的一组整数解 \(x_0, y_0\) ,记 \(d = \gcd(a, b)\) 。怎么由一组特解推出通解呢?
由观察可知,当 \(x\) 变大时, \(y\) 会变小。因此设 \(x_0 + kp, y_0 - kq\) 为另外一组解 ( \(p, q\) 为一个定值),我们通过改变 \(k\) 来求得每一组解。这个时候可能聪明的你会感觉到有些不对劲:为什么 \(p\) 和 \(q\) 的系数绝对值刚好都是 \(k\) 呢?就不能 \(x + (k + 1)p, y_0 - kq\) 吗? \(p, q\) 又可以是什么定值呢?我们来证明一下:
不管\(k\),我们可以列出方程组:
可以解得 \(ap = bq, p = \frac{bq}{a}, q = \frac{ap}{b}\) 。也就是 \(p, q\) 需要满足这个等式才能构造出另外一组解。但还不够,我们需要 \(p, q\) 为最小的正整数 \(p, q\) ,以此来通过调整系数 \(k\) 求出每一组解,要求 \(a \mid bq\) , \(b \mid ap\) 。所以 \(bp_{\min} = \operatorname{lcm}(a, b) = \frac{ab}{d}\) (此处详见下面的最大公倍数),\(p_{\min} = \frac{a}{d}\) 。同理 \(q_{\min} = \frac{b}{d}\) 。我们就求出了 \(p, q\) 的最小正整数。
我们设另一组解为 \(x_0 + np, y_0 - mq\) ,则:
解得 \(anp = bmq\) 。又由上面的 \(ap = bq\) ,我们就会发现 \(n = m\) ,所以 \(k = n = m\) 就好啦!
之后具体细节分类讨论即可(然后就调了一上午) 。推荐题解第一篇,感觉最简单易懂!(加 longlong, 不然就会在不知不觉中浪费1个小时QAQ)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 19260817
int a, b, c;
int exgcd(int a, int b, int &x, int &y){
if(!b){
x = 1, y = 0;
return a;
}
int a1 = exgcd(b, a % b, x, y);
int x1 = y, y1 = x - a / b * y;
x = x1, y = y1;
return a1;
}
signed main(){
// freopen("shuju.in", "r", stdin);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int TN;
cin >> TN;
while(TN--){
cin >> a >> b >> c;
int x, y;
int gcd = exgcd(a, b, x, y);
x *= c / gcd, y *= c / gcd;
int p = b / gcd, q = a / gcd;
if(c % gcd) {
cout << -1 << endl;
continue;
}
int sl = ceil((1.0 - x) / p), sr = floor((y - 1.0) / q);
if(sl > sr) {
cout << x + sl * p << " " << y - sr * q;
}
else{
cout << sr - sl + 1 << " ";
cout << x + sl * p << " " << y - sr * q << " " << x + sr * p << " " << y - sl * q;
}
cout << endl;
}
return 0;
}
欧拉定理
当 \(a \bot m\) 时, $$a ^ {\varphi(m)} \equiv 1 \pmod m$$
[[欧拉函数&欧拉定理]]
详细证明可见 欧拉函数 & 欧拉定理
证明:
我们构造一个数 \(k\) ,只需使得 \(ka ^ {\varphi(m)} \equiv k\) ,且 \(k \bot m\) 即可证明欧拉定理。设在模 \(m\) 下的剩余系中与 \(m\) 互质的数的集合为 \(\left\{ b_1, b_2, ... b_n \right\} (n = \varphi(m))\) ,令 \(k = b_1b_2...b_n\) ,这时 \(k \bot m\) 。又因为 \(a \bot m\) , 所以集合 \(\left\{ ab_1, ab_2, ..., ab_n\right\}\) 中每个数都与 \(m\) 互质,即 \(\left\{ b_1, b_2, ... b_n \right\} = \left\{ ab_1, ab_2, ..., ab_n\right\}\) 。那 \(ab_1 * ab_2* ...*ab_n = a ^ {\varphi(m)}k \equiv k\) 。证毕。
由欧拉定理,我们能直接得出费马小定理,实际上欧拉定理就是费马小定理的推广。
最小公倍数
我们已经知道可以通过 \(\gcd\) 求出 \(a, b\) 的最大公因数,那么有没有类似的快捷方法求出 \(a, b\) 的最大公倍数呢?
当然有!通过算术基本定理,我们将 \(a, b\) 表示成如下形式:
其中 \(p\) 属于 \(a, b\) 的所有质因数集合。
那么
而
于是
Lucas定理
前置知识:二项式定理
可以看看容斥定理及二项式反演
Lucas定理:
其中 \(p\) 是质数。
可以看成是 \(n, m\) 在 \(p\) 进制下各数码的组合乘积。
证明(不要被式子吓到啦,很简单的):
\(\binom nm\) 可以看成 \((1+x)^n\) 中 \(x^m\) 项的系数,我们就尝试写一下:
因为 \(p\) 是质数,所以对于 \(\forall i \in [1,p-1]\) ,\(i\) 在模 \(p\) 下均有逆元,\(\binom pi = \frac{p}{i}\binom{p-1}{i-1} \equiv 0 \pmod p\)
因为中间的系数在模 \(p\) 下为 \(0\) ,在 \((1+x)^n\) 中相加不会对模 \(p\) 后 \(x^m\) 项的系数有所影响,就可以直接忽略。于是我们继续:
发现这个式子要想凑出 \(x^m\) 那一项,因为左边只能提供 \(p\) 的倍数次项,右边只能提供模 \(p\) 的余数的项,所以对 \(x^m\) 的系数有贡献的项必须是是左边提供 \(\lfloor\frac{m}{p}\rfloor\) 的项, 右边提供 \(m \mod p\) 的项,所以 \(x^m\) 项的系数在模 \(p\) 下就是
证完!
素数的线性筛法
为了让一个合数只被标记一次,我们设法让一个合数只会被它的最小素因子标记一次。
vector<int> p;
int tag[N];
void init(int n){
tag[1] = 1;
for(int i = 2; i <= n; i ++){
if(!tag[i]) p.push_back(i);
for(auto j : p){
if(j * i > n) break;
tag[i * j] = 1;
if(i % j == 0) break;
//这里我们假设i = k*j, 下一个素数为g, g > j
//i*g = k*j*g, 因为k*g > i,所以i*g在之后循环到k*g时被标记,这里就不用标记了
}
}
}
整除分块
能够在 \(O(\sqrt n)\) 的时间内求出 \(\sum_{i=1}^n \frac{n}{i}\) 。
正常肯定是 \(O(n)\) 的,但是考虑一下如何加快。随便画一个反比例函数图像,会发现有对于每个 \(i\) ,几乎都会有一段连续区间 \(\frac{n}{i}\) 的值是相同的。结论是,对于 \(i\) 所在的块,块的右端点是 \(\frac{n}{\lfloor\frac{n}{i}\rfloor}\) 。
另外一个结论,$$\sum_{i=1}^n[x\mid i] = \lfloor\frac{n}{x}\rfloor$$
int g(int n){
int ans = 0;
for(int l = 1, r; l <= n; l = r + 1){
r = n / (n / l);
ans += (r - l + 1) * n / l;
}
return ans;
}
威尔逊定理
\(p\) 为素数等价于

浙公网安备 33010602011771号