数论基础+裴蜀定理+线性同余方程+乘法逆元+中国剩余定理

本文内容大多搬运来自oi wiki!

数论基础

整除

整除的定义:设 \(a,b\in\mathbf{Z},a\ne 0\)。如果 \(\exists q\in\mathbf{Z}\),使得 b=aq,那么就说 b 可被 a 整除,记作 \(a\mid b\);b 不被 a 整除记作 \(a\nmid b\)

同余

同余的定义:设整数 \(m\ne0\)。若 \(m\mid(a-b)\),称 m 为模数(模),a 同余于 b 模 m,b 是 a 对模 m 的剩余。记作 \(a\equiv b\pmod m\)
否则,a 不同余于 b 模 m,b 不是 a 对模 m 的剩余。记作 \(a\not\equiv b\pmod m\)
这样的等式,称为模 m 的同余式,简称同余式。
根据整除的性质,上述同余式也等价于 \(a\equiv b\pmod{(-m)}\)
如果没有特别说明,模数总是正整数。
式中的 b 是 a 对模 m 的剩余,这个概念与余数完全一致。通过限定 b 的范围,相应的有 a 对模 m 的最小非负剩余、绝对最小剩余、最小正剩余。


裴蜀定理

设 a,b 是不全为零的整数,则存在整数 x,y, 使得 \(ax+by=\gcd(a,b)\)
进一步:
设自然数 a、b 和整数 n。a 与 b 互素(前提)。考察不定方程:ax+by=n,其中 x 和 y 为自然数。如果方程有解,称 n 可以被 a、b 表示。
记 C=ab-a-b。由 a 与 b 互素,C 必然为奇数。则有结论:对任意的整数 n,n 与 C-n 中有且仅有一个可以被表示。
即:可表示的数与不可表示的数在区间 [0,C] 对称(关于 C 的一半对称)。0 可被表示,C 不可被表示;负数不可被表示,大于 C 的数可被表示。

进一步的例题

洛谷 P3951 买不到的数目

相关资料

524 裴蜀定理


求不定方程

\(ax+by=gcd(a,b)\)

已知a,b,求解\(ax+by=gcd(a,b)\)的一组整数解
利用扩展欧几里得算法求解,原理可见相关资料

int gcd(int a, int b){ if(b == 0) return a; else return gcd(b, a % b); }

int gcd(int a,int b){
	if(b==0) return a;
	else return gcd(b,a%b);
}

int exgcd(int a,int b,int &x,int &y){//返回gcd
	if(b == 0) {x=1, y=0; return a;}
	int x1, y1, d;
	d = exgcd(b, a%b, x1, y1);
	x = y1, y = x1-a/b*y1;
	return d;
}

void exgcd(int a,int b,int &x,int &y){//不返回gcd
	if(b==0){
		x=1; y=0; return ;
	}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
	return ;
}

最终的\((x_0,y_0)\)即为特解,通解的构造利用 \(ax+by=0\) ,可得$x=x_0+\frac{b}{gcd(a,b)} * k ;y=y_0-\frac{a}{gcd(a,b)} * k $

\(ax+by=c\)

已知a,b,c,求解\(ax+by=c\)的一组整数解

  • \(gcd(a,b) \mid c\),则先利用exgcd求得\(ax+by=gcd(a,b)\)的解,再乘以\(c / gcd(a,b)\),即得原方程的特解$(x_0,y_0) $
  • 反之,即\(gcd(a,b) \nmid c\),则无整数解

相关资料

525 不定方程 扩展欧几里得算法原理
扩展欧几里得算法(简单易懂,详细分析)


线性同余方程

形如 \(ax\equiv b\pmod m\) 的方程称为 线性同余方程(Congruence Equation)。其中,a、b 和 m 为给定整数,x 为未知数。需要从区间 [0, n-1] 中求解 x,当解不唯一时需要求出全体解。

利用exgcd求解线性同余方程

image
代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int exgcd(int a,int b,int &x,int &y){
	if(b == 0){x = 1, y = 0; return a;}
	int x1, y1, d;
	d = exgcd(b, a%b, x1, y1);
	x = y1, y = x1-a/b*y1;
	return d;
}
int main(){
	int a, b, m, x, y;
	scanf("%d%d%d", &a, &b, &m);
	int d = exgcd(a, m, x, y);
	if(b%d == 0) 
		printf("%d", 1ll*x*b/d%m);
	else puts("none");
	return 0;
}

相关资料

526 同余方程 乘法逆元 扩展欧几里得算法


乘法逆元

求解数n在模 p 意义下的乘法逆元

当a与m互质时,对于方程\(ax\equiv 1\pmod m\),求a的乘法逆元x(0<x<m)
解法一: 费马小定理(当m为质数时)
数a的乘法逆元\(x=a^{m-2}\)
可利用快速幂方便求解
故,详见快速幂板子

解法二: 扩展欧几里得法(当\(gcd(a, m) = 1\)时)

  • 先将乘法逆元形式转化为不定方程形式,变形等价\(ax+my=1\)
  • 再利用exgcd求解\(ax+my=gcd(a,m)\)的解x,那么\((x\%m+m)\%m\)即为最终答案

exgcd板子:

void exgcd(int a, int b, int & x, int & y){
	if(b == 0){
		x = 1;
		y = 0;
		return;
	}
	exgcd(b, a % b, y, x);
	y -= a / b * x;
	return;
}

cout << (p + x % p) % p << '\n';

线性求解 \(1,2,\dots,n\) 中每个数关于 p 的逆元

迭代实现

inv[1] = 1;
for(int i = 2; i <= n; ++ i){
	inv[i] = (p - p / i) * inv[p % i] % p;
	cout << inv[i] << '\n';
}

相关资料

  1. https://oi-wiki.org/math/number-theory/inverse/
  2. bilibili 视频

取余题目集合

  1. https://atcoder.jp/contests/abc275/tasks/abc275_b 小坑勿怪!!!
    代码——qiansui_code
  2. https://loj.ac/p/110 模板题
    代码——qiansui_code——扩展欧几里得法
    代码——qiansui_code——费马小定理
    3.有意思的一题:
    https://atcoder.jp/contests/abc298/tasks/abc298_d
    运用到取余+快速幂+思维

中国剩余定理 CRT

中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中 \(n_1\), \(n_2\), \(\cdots\), \(n_k\) 两两互质):

\[\begin{cases} x &\equiv a_1 \pmod {n_1} \\ x &\equiv a_2 \pmod {n_2} \\ &\vdots \\ x &\equiv a_k \pmod {n_k} \\ \end{cases} \]

上面的「物不知数」问题就是一元线性同余方程组的一个实例。

求解过程:

  • 计算所有模数的积 n;
  • 对于第 i 个方程:
    计算 \(m_i=\frac{n}{n_i}\)
    计算 \(m_i\) 在模 \(n_i\) 意义下的 逆元 \(m_i^{-1}\)
    计算 \(c_i=m_im_i^{-1}\)(不要对 \(n_i\) 取模)
  • 方程组在模 n 意义下的唯一解为:\(x=\sum_{i=1}^k a_ic_i \pmod n\)

板子:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
 
typedef long long ll;
ll n, a[11], b[11];

ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){x=1, y=0; return a;}
	ll d, x1, y1;
	d = exgcd(b, a%b, x1, y1);
	x = y1, y = x1-a/b*y1;
	return d;
}
ll CRT(ll m[], ll r[]){
	ll M = 1, ans = 0;
	for(int i=1;i<=n;i++) M*=m[i];
	for(int i=1; i<=n; i++){
		ll c = M/m[i], x, y;
		exgcd(c, m[i], x, y);
		ans = (ans+r[i]*c*x%M)%M;
	}
	return (ans%M + M)%M;
}
int main(){
	scanf("%lld", &n);
	for(int i = 1; i <= n; ++i)
		scanf("%lld%lld", a+i, b+i);
	printf("%lld\n", CRT(a,b));
	return 0;
}

相关资料

527 中国剩余定理

posted @ 2022-10-30 23:58  Qiansui  阅读(121)  评论(0)    收藏  举报