暑假集训 数论基础

数论基础

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)\)
posted @ 2021-08-04 22:53  !^^!  阅读(173)  评论(0)    收藏  举报