中国剩余定理一般用于求解模数互质线性同余方程组。形式如下:
{ x ≡ a 1 ( m o d b 1 ) x ≡ a 2 ( m o d b 2 ) … x ≡ a i ( m o d b i ) … x ≡ a n ( m o d b n ) \begin{cases} x \equiv {a_1} \pmod {b_1} \\ x \equiv {a_2} \pmod {b_2} \\ \ldots \\ x \equiv {a_i} \pmod {b_i} \\ \ldots \\ x \equiv {a_n} \pmod {b_n} \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧x≡a1(modb1)x≡a2(modb2)…x≡ai(modbi)…x≡an(modbn)
首先设 M M M为所有模数的乘积,即 ∏ i = 1 n b i \prod \limits_{i=1}^{n} b_i i=1∏nbi。
我们构造一个 x = ∑ i = 1 n a i ⋅ t i ⋅ m i x=\sum \limits_{i=1}^{n} {a_i} \cdot {t_i} \cdot {m_i} x=i=1∑nai⋅ti⋅mi,其中 m i = M b i m_i=\frac{M}{b_i} mi=biM, t i ⋅ m i ≡ 1 ( m o d b i ) t_i\cdot m_i \equiv 1 \pmod{ b_i} ti⋅mi≡1(modbi),可以证明 x x x为方程组的一个特解。证明如下:
由于 m i = M b i m_i=\frac{M}{b_i} mi=biM,且模数均两两互质,那么其余模数必然是 b i b_i bi的倍数。又因为 t i ⋅ m i ≡ 1 ( m o d b i ) t_i\cdot m_i \equiv 1 \pmod{ b_i} ti⋅mi≡1(modbi),所以 x ≡ a i ( m o d b i ) x \equiv a_i \pmod{b_i} x≡ai(modbi)。得证。
观察 x x x的构造过程,发现只有 t i t_i ti是未知量,于是我们可以单独对于每一个方程,利用扩展欧几里得算法,求出每一个 b i b_i bi对应的 t i t_i ti,同时 a n s ans ans累加上 a i ⋅ t i ⋅ m i {a_i} \cdot {t_i} \cdot {m_i} ai⋅ti⋅mi。
中国剩余定理板子题,不过代码中的数组名称与上文的描述有些区别。
还是模板题。
//曹冲养猪
#include
#define ll long long
#define MAXN 15
using namespace std;
int n;
ll mod[MAXN], b[MAXN], ans;
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1;
y = 0;
return a;
}
ll res = exgcd(b, a%b, x, y);
ll t = x;
x = y;
y = t-a/b*y;
return res;
}
void solve(){ //中国剩余定理
ll m = 1;
for(int i = 1; i <= n; i++){
m *= mod[i];
}
ll x, y, mi;
for(int i = 1; i <= n; i++){
mi = m/mod[i];
exgcd(mi, mod[i], x, y);
ans += (mi*x*b[i]);
ans %= m;
}
ans = (ans+m)%m;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%d%d", &mod[i], &b[i]);
}
solve();
cout << ans << endl;
return 0;
}
为什么需要这样一个扩展的定理呢?很明显,中国剩余定理的描述中有一个至关重要的限制条件:模数两两互质。 然而实际上,即使模数不互质,也能对方程组进行求解。这就是扩展中国剩余定理。但实际上,除了解决的问题基本相同,扩展中国剩余定理在证明和实现上都不仅仅是“扩展”,所以XX通索性称其为扩展欧几里得解线性同余方程。
首先,我们改变一些定义,设当前正在处理第 i i i个方程,则令 M M M表示前 i − 1 i-1 i−1个模数的最小公倍数。令 x x x为前 i − 1 i-1 i−1个方程的特解, x ′ x' x′为所求的前 i i i组方程的特解。于是对于第 i i i个方程,问题转化为求一个 t t t,使得 x + m ∗ t ≡ a i ( m o d b i ) {x+m*t} \equiv {a_i} \pmod {b_i} x+m∗t≡ai(modbi),而 x ′ = x + m ∗ t x'={x+m*t} x′=x+m∗t。
该方程等价于 m ∗ t ≡ a i − x ( m o d b i ) m*t \equiv {a_i-x} \pmod {b_i} m∗t≡ai−x(modbi),显然只有 t t t是未知量于是我们就可以利用扩欧求出 t t t。一直重复 n n n遍,最终就可以得到方程组的解。
真·模板题。
#include
#define ll long long
#define MAX 100005
using namespace std;
ll n, m, ans;
ll a[MAX], b[MAX];
ll exgcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1, y = 0;
return a;
}
ll res = exgcd(b, a%b, x, y);
ll t = x;
x = y, y = t-a/b*y;
return res;
}
ll mul(ll a, ll b, ll mod){
ll res = 0, base = a;
while(b){
if(b&1){
res = (res+base)%mod;
}
base = (base+base)%mod;
b >>= 1;
}
return res;
}
ll excrt(){
m = b[1], ans = a[1];
for(int i = 2; i <= n; i++){
ll p = ((a[i]-ans)%b[i]+b[i])%b[i];
ll x, y; //mx=p (mod b[i])
ll gcd = exgcd(m, b[i], x, y);
if(p % gcd) return -1;
x = mul(x, p/gcd, b[i]/gcd);
ans += x*m;
m *= b[i]/gcd;
ans = (ans%m+m)%m;
}
return (ans%m+m)%m;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%lld%lld", &b[i], &a[i]);
}
printf("%lld\n", excrt());
return 0;
}