表达整数的奇怪方式(EXCRT
给定2n个整数,a1 , a2 , ...... , an 、m1 , m2 , ...... , mn,
求一个最小的正整数x,满足∀ 1≤i≤n , x ≡ mi (mod ai)
如果不存在解输出"-1",否则输出最小的正整数x
数据范围:
1 ≤ ai ≤ 231-1
1 ≤ mi < ai
1 ≤ n ≤ 25
# 题解
先考虑前两个式子:
x ≡ m1 (mod a1) ↔ x = k1 * a1 + m1
x ≡ m2 (mod a2) ↔ x = k2 * a2 + m2
k1 * a1 + m1 = k2 * a2 + m2
k1 * a1 -k2 * a2 = m2-m1
判断一下有没有解即 gcd(a1,a2) |(m2-m1) 是否成立
扩展欧求出 k1 * a1 -k2 * a2 = gcd(a1,a2) 的解k1''和k2''
k1'=k1'' * (m2-m1)/gcd(a1,a2)
k2'=k2'' * (m2-m1)/gcd(a1,a2)
由x = k1 * a1 + m1或x = k2 * a2 + m2都可以得到x的通解
其中k1通解=k1 + k* (a2/gcd(a1,a2))显然k1取得最小值的时候x最小
但是在和后面的式子合并的时候有了新的约束,
x的最小值可能会改变所以先不具体求出x,
用得到的k1或k2的通解构成一个新的同余等式
将k1通解=k1' + k* ( a2/gcd(a1,a2) )带入x = k1 * a1 + m1
x = ( k1' + k* ( a2/gcd(a1,a2) ) ) * a1 + m1
x = k * ( a1 * a2 )/gcd(a1,a2) + k1'*a1+m1
a'=( a1 * a2 )/gcd(a1,a2) , m'=k1'*a1+m1
x = k * a' + m'
x = k3 * a3 + m3
不断的合并到k-1,根据k的通解求得最小的x,
k = k0 + t * (an/gcd(a',an))
显然k0 % (an/gcd(a',an)) 即最小的解
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 ll n; 5 ll exgcd(ll a,ll b,ll &x,ll &y){ 6 if(b==0){ 7 x=1;y=0; 8 return a; 9 } 10 ll d=exgcd(b,a%b,y,x); 11 y-=(a/b)*x; 12 return d; 13 } 14 int main(){ 15 cin>>n; 16 ll a1,m1; 17 cin>>a1>>m1; 18 for(int i=0;i<n-1;i++){ 19 ll a2,m2,k1,k2; 20 cin>>a2>>m2; 21 ll d=exgcd(a1,a2,k1,k2);//式子中的a2系数当作正数传入,得到的k可能为负数 22 if((m2-m1)%d) {puts("-1");return 0;} 23 k1*=(m2-m1)/d;//由裴蜀定理的解转化为方程解 24 ll t=(a2/d); 25 k1=(k1%t+t)%t;//对负数取模 26 m1=k1*a1+m1; 27 a1=a1/d*a2; 28 } 29 cout<<(m1%a1+a1)%a1<<endl; 30 return 0; 31 }