暑假集训 数论基础
数论基础
gcd
最大公约数 \((Greatest Common Divisor)\),常缩写为 \(gcd\) 。
欧几里得算法(辗转相除法)
如果已知两个数,求两者的最大公约数 \(gcd(a,b)\) 。
设 \(a>=b\),如果 \(b\) 是 \(a\) 的因数,则 \(b\) 为 \(a\) 的最大公约数。
下面讨论 \(b\) 不是 \(a\) 的因数的情况:
我们通过证明可以得到 \(gcd(a,b) = gcd(b, a\ mod\ b)\),证明过程如下:
设 \(a = bk + c\),显然有 \(c = a\ mod\ b\)。
设 \(d\ |\ a,\ d\ |\ b\),则有: \(\cfrac{c}{d} = \cfrac{a}{d} - \cfrac{b}{d} k\) 。所以 \(\cfrac{c}{d}\) 为整数,即 \(a,b\)的公因子也会是 \(b\) 和 \(a\ mod\ b\)的因子。
反过来:
设 \(d\ |\ b,\ d\ |\ (a\ mod\ b)\),如上所述: \(\cfrac{a\ mod\ b}{d} = \cfrac{a - bk}{d} = \cfrac{a}{d} - \cfrac{b}{d} k\) 。即: \(\cfrac{a}{d} = \cfrac{(a\ mod\ b)}{d} + \cfrac{b}{d}k\),故 \(\cfrac{a}{d}\)也为整数,即 \(b,a\ mod\ b\)的公因数也是 \(a,b\)的公因数。
故两式相等。
因此有 \(gcd\) 的求法:
int gcd(int a,int b){
b ? gcd(b, a % b) : a;
}
lcm
最小公倍数: \(lcm(a,b) = a * b / gcd(a,b)\) ;
素数筛
如果想知道小于等于n有多少个素数,需要使用素数筛算法。
埃拉托斯特尼筛法(埃氏筛)
对于一个质数 \(x\) 来说,\(x\) 的倍数一定是合数。所以我们可以从小到大考虑每个数,然后把当前这个数的所有倍数记为合数,运行结束后没有被标记的数就是素数。
时间复杂度: \(O(nlog(logn))\)
int Eratosthenes(int n) {
int p = 0;
for (int i = 0; i <= n; ++i) is_prime[i] = 1;
is_prime[0] = is_prime[1] = 0;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
prime[p++] = i; // prime[p]是i,后置自增运算代表当前素数数量
for (int j = i * i; j <= n; j += i)
// 因为从 2 到 i - 1 的倍数我们之前筛过了,这里直接从 i
// 的倍数开始,提高了运行速度
is_prime[j] = 0; // 是i的倍数的均不是素数
}
}
return p;
}
欧拉筛(线性筛)
埃氏筛会把一个合数多次标记,(比如 \(12\) 会被 \(2\) 和 \(3\) 重复标记),所以时间复杂度仍有优化空间。
我们只要做到使每个合数都只用其最小的一个质因子标记,就可以达到 \(O(n)\)
void init(){
vis[1] = 1;
for(int i = 2; i < maxn; i++){
if(!vis[i]){
prime[cnt++] = i;
}
for(int j = 0; j < cnt; j++){
if(i * prime[j] > maxn)
break;
vis[i * prime[j]] = 1;
if(i % prime[j] == 0)
break;
}
}
}
Min_25筛
更快的筛法,如果感兴趣可以自行了解。
分解质因子
因数是成对存在的,\(N\) 的所有因数都可以分成两块,即 \([1,\sqrt{N}]\) 和 \([\sqrt{N} + 1, N]\)。只需要把 \([1,\sqrt{N}]\) 中的因数遍历一遍,再根据除法就可以找到至少两个因数。
vector<int> breakdown(int N){
vector<int>ans;
for(int i = 2; i * i <= N;i ++){
if(N % i == 0){
while(N % i == 0) N /= i;
ans.push_back(i);
}
}
if(N > 1){
ans.push_back(N);
}
return ans;
}
欧拉函数
欧拉函数(Euler's totient function),即: \(\varphi(x)\),表示小于等于 \(n\)和 \(n\)互质的数的个数。比如 \(\varphi(1) = 1\)
当 \(n\)为质数时,显然有 \(\varphi(n) = n - 1\)。
对于欧拉函数,主要有以下性质:
- 若 \(p\) 是质数,则 \(\varphi(x) = p^{n-1}(p - 1)\)
- 若 \(a\ |\ x\),则 \(\varphi(ax) = a\varphi(x)\)
- 若 \(a,b\) 互质,则 \(\varphi(a)\varphi(b) = \varphi(ab)\)
公式: \(\varphi(x) = n * \prod_{i = 1} ^ {n} \cfrac{p_i - 1}{p_i}\)。(\(p_i\) 标识 \(n\) 的所有质因子)。
int euler_phi(int n){
int ans = n;
for(int i = 2; i * i <= n; i++){
if(n % i == 0){
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1)
ans = ans / n * (n - 1);
return ans;
}
如果要求多个数的欧拉函数值,可以使用线性筛同时维护每个数的欧拉函数。
void init(){
int cnt = 0;
phi[1] = 1;
vis[0] = 1;
for(int i = 2; i < maxn; i++){
if(!vis[i]){
prime[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt;j ++){
if(i * prime[j] > maxn)
break;
vis[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;
}
}
}
}
快速幂
快速幂,也叫二进制取幂,可以在 \(O(logn)\)的时间内计算 \(a^n\) 的小技巧,而暴力计算则需要 \(O(n)\)的时间。
\(a^n\) = \(a * a * …… * a\)( \(n\)个 \(a\)) = \(a^2 * …… * a^2\) ( \(n/2\) 个 \(a^2\)) = \(……\) = \((a^n)^1\),总而计算出最后结果。
乘法逆元
如果一个线性同余方程 \(ax = 1(mod\ b)\),则称 \(x\)为 \(a\ mod\ b\)的逆元,记作:\(a^-1\)。
费马小定理
因为 \(ax = 1 (mod\ b)\);
所以 \(ax = a^{b-1} (mod\ b)\)
所以 ** \(x = a^{b-2} (mod\ b)\) **
inline int qpow(long long a, int b){
int ans = 1;
a = (a % p + p) % p;
for(; b;b >>= 1){
if(b & 1)
ans = (a * ans) % p;
a = (a * a) % p;
}
return ans;
}
这里费马小定理有一个限制就是b必须是一个素数。
扩展欧几里得法
只需要gcd(a,p) = 1即可。
方程 \(ax + py = 1\) 与 \(ax = 1(mod\ p)\) 是等价的 。
引理: 存在 \(x,y \in Z\) 使得 \(ax + by = gcd(a,b)\) (具体证明见下面裴蜀定理)。
- 当 \(b = 0\) 时,\(gcd(a,b)\) = a,此时 \(x_1 = 1,y_1 = 0\) 。
- 当 \(b \not= 0\) 时,
- \(ax + by = gcd(a,b) = gcd(b,a\ mod\ b) = bx_2 + (a\ mod\ b)y_2\)
- 又因为 \(a\ mod\ b = a - \lfloor \cfrac{a}{b} \rfloor b\),所以 \(ax + by = bx_2 + (a - a\lfloor \cfrac{a}{b} \rfloor b)y_2\)
即: \(ax + by = ay_2 + b(x2 - \lfloor \cfrac{a}{b} \rfloor y_2)\)
通过对比得出 \(gcd(a,b) = ax + by\)的通解为 \(x = y_2, y = x_2 - \lfloor \cfrac{a}{b} \rfloor y_2\) 。
当求出一组 \(x_0, y_0\),也就是 \(ax_0 + by_0 = gcd(a,b)\),然后两边同时除以 \(gcd(a,b)\),再乘 \(c\)。 然后就得到了方程 \(a\cfrac{c}{gcd(a,b)}x_0 + b\cfrac{c}{gcd(a,b)}y_0 = c\),即找到了一组解
若 \(gcd(a,b) = 1\),且 \(x_0,y_0\)为方程 \(ax + by = c\)的一组解,则该方程的任意解可表示为: \(x = x_0 + bt, y = y_0 - at\),若要找到最小整数解,也即是一个特解: \(x = (x\ mod\ t + t)\ mod\ t\),其中 \(t = \cfrac{b}{gcd(a,b)}\)。
int exgcd(int &x,int &y, int a,int b){
if(!b){
x = 1;
y = 0;
return a;
}
int r = exgcd(x,y,b,a%b);
int t = x;
x = y;
y = t - a / b * y;
return r;
}
裴蜀定理
又称贝祖定理(Bézout's lemma)。是一个关于最大公约数的定理。
其内容是:
设 \(a,b\) 是不为零的整数,则存在整数 \(x,y\),使得 \(ax + by = gcd(a,b)\) 。
证明
-
若任何一个等于零,则 \(gcd(a,b) = a\) (设 \(a >= b\)),显然成立
-
若 \(a,b\) 不等于0.
\(gcd(a,b) = gcd(a,-b)\),
不妨设 \(a,b\) 都大于0, \(a >= b, gcd(a,b) = d\) 。
对 \(ax + by = d\),两边同时除以 \(d\),可得 \(a_1x + b_1y = 1\),其中 \(gcd(a_1,b_1) = 1\).
转证 \(a_1x + b_1y = 1\).
因为 \(gcd(a,b) = gcd(b, a\ mod\ b) = ……\),于是有:
\(gcd(a_1,b_1) = gcd(b_1,r_1) = gcd(r_1,r_2) = …… = gcd(r_{n-1},r_n) = 1\)
把辗转相除法中的运算展开,得:\(a_1 = x_1b + r_1\)
\(b_1 = x_2r_1 + r_2\)
\(r_1 = x_3r_2 + r_3\)
……
\(r_{n-3} = x_{n-1}r_{n-2} + r_{n-1}\)
\(r_{n-2} = x_nr_{n-1} + r_n\)
\(r_{n-1} = x_{n+1}r_n\)定义辗转相除法在 \(r_n = 1\)时退出
则有: \(r_{n-2} = x_nr_{n-1} + 1\),同时由倒数第三个式子 \(r_{n-1} = r_{n-3} - x_{n-1}r_{n-2}\) 代入上式得: \(1 = (1 + x_nx_{n-1}) r_{n-2} - x_nr_{n-3}\)
同理可消去 \(r_{n-2},…,r_1\),最后得出: \(1 = a_1x + b_1y\)。
故得证。
中国剩余定理
求解如下形式的一元线性同余方程组(其中 \(n_1,n_2,n_3,……,n_k\) 两两互质):
\(x = a_1 (mod\ n_1)\)
\(x = a_2 (mod\ n_2)\)
……
\(x = a_k (mod\ n_k)\)
** 解决方法 **
- 计算所有模数(\(n_i\))的积 \(n\);
- 对于每个方程计算 \(m_i = \cfrac{n}{n_i}\);
- 计算 \(m_i\) 在模 \(n_i\)的意义下的逆元 \(m_i^{-1}\);
- 计算 \(c_i = m_im_i^{-1}\) (不要对 \(n_i\) 取模)
- 方程组的唯一解为: \(x = \sum_{i = 1}^k a_ic_i (mod\ n)\) 。

浙公网安备 33010602011771号