贴一个大神的数论知识点整理博客:https://blog.youkuaiyun.com/WhereIsHeroFrom/article/details/111693538
中国剩余定理
参考:中国剩余定理(孙子定理)
参考:中国剩余定理(详解)
参考:中国剩余定理算法详解(余数互质和不互质)
注:理解时按上面的顺序看博客
假设( a[] 为模 ,b[] 为余数)( a[] 互质情况下)
x % a1 = b1
x % a2 = b2
x % a3 = b3
求x
由 a2*a3*k 为 x%a1余1的最小的数 (除a1外的a2 a3的符合要求的最小公倍数)
得 a2*a3*k*b1 为 x%a1 余 b1 的最小的数 (对应上面第一个式子)
那么 x = a2*a3*k1*b1 + a1*a3*k2*b2 + a1*a2*k3*b3 -(a1*a2*a3)*k4
a2*a3*k1 的结果可由exgcd求得:
a2*a3*k ≡ 1(mod a1)
用 m 表示a2*a3, ,用n表示a1
就可以写成 m*k ≡ 1 (mod n)
也就是 m*k - n*y = 1
而 m 的倍数 我们不确定所以式子:
k*m - y*n =1 (扩展欧几里得)
求出上式 k,就能求出 a2*a3*k
x = xx_exgcd(a2*a3*k, a1, xx, yy) * a2*a3*b1 + ...
//互质的中国剩余定理
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
int Gcd = exgcd(b, a % b, x, y);
int t = y;
y = x - (a / b) * y;
x = t;
return Gcd; //a b的最大公因数
}
int math(int a[], int m[]) // a[]为余数 ,m[]为除数
{
int x = 0;
int cj = 1;
for (int i = 0; i < n; i++)
cj *= m[i];
for (int i = 0; i < n; i++)
{
int t = cj / m[i], xx, yy;
exgcd(t, m[i], xx, yy);
x = (x + t * xx % cj * a[i]) % cj;
}
return (x % cj + cj) % cj;
}
signed main()
{
int a[15], b[15]; //a模 b余数
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i] >> b[i];
cout << math(b, a) << endl;
return 0;
}
扩展中国剩余定理 (不互质)
将式子合并(以前两个为例):
x = a1 mod m1
x = a2 mod m2
-> k1m1 - k2m2 = a2 - a1
-> k1m1 = a2 -a1 mod(m2)
上式有解时: gcd(m1,m2) | (a2 -a1),即(a2-a1)能整除m1和m2的最大公约数
令 d = gcd(m1,m2) ,c = a2 - a1
-> k1m1 = c mod (m2)
将上式化简(去掉式子的最大公约数):k1m1/d = c/d mod(m2/d)
下一步是求出k1的值,带入第三行的式子
化简式子后,可发现两个式子通过一系列代换可合并为一个(式子中的变量不同)
求 k1 用到了(m1/d)关于(m2/d)的逆元
-> k1 = 逆元*(c/d) ----------------(c/d在上段最后一行)
求逆元:
由 ax = 1 mod(b)
-> m1/d ≡ 1 mod(m2/d)
利用扩展欧几里得: ax + by = gcd(a,b)
将 a看作(m1/d) , b看作(m2/d) :
也就是 (m1/d)*x + (m2/d)*y = 1
-> 逆元xx 就是 exgcd(m1/d,m2/d,xx,yy) 式子中的xx(记为 K)(注:m1/d和m2/d互质)
求k1:
k1 = K*(c/d) mod(m2/d) = k*(c/d) + y*(m2/d)
将k1带入第一个式子:x = a1 mod(m1)
-> x = k1m1 + a1
-> x = ( K*(c/d) mod(m2/d) )*m1 + a1
-> x = K*(c/d)*m1 + a1 mod(m1*m2/d)
与第一个式子对比:x = a mod m
通式:a = K*(c/d)*m1+a1 , m = m1*m2/d
依次类推...
扩展中国剩余定理模板题:
204. 表达整数的奇怪方式 : https://www.acwing.com/problem/content/description/206
P1495 【模板】中国剩余定理(CRT)/曹冲养猪:https://www.luogu.com.cn/problem/P1495
#include <bits/stdc++.h>
using namespace std;
long long a[30],m[30];
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
long long Gcd = exgcd(b, a % b, x, y);
long long t = y;
y = x - (a / b) * y;
x = t;
return Gcd; //a b的最大公因数
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>m[i]>>a[i]; // m是模 ,a是余数!!
long long a1,a2,m1,m2,c,xx,yy;
a1=a[0],m1=m[0];
for(int i=1;i<n;i++)
{
a2=a[i],m2=m[i];
c= a2- a1;
long long gcd=__gcd(m1,m2);
if( c % gcd !=0)
{
cout<<"-1"<<endl;
return 0;
}
exgcd(m1/gcd,m2/gcd,xx,yy);
xx = c / gcd * xx;
xx = (xx% (m2/gcd) +(m2/gcd)) %(m2/gcd); //化为正数
a1 += xx * m1 ;
m1 = m2 /gcd * m1 ;
}
cout<<a1<<endl;
return 0;
}