题目:
对于正整数a1, a2, …, ak,用m模得到对应余数 ri
现在已知所有的整数对 (ai, ri) ,求对应的m
INPUT:
多组测试数据,每一组,第一行是k,后面k行每行是一个整数对 (ai, ri)
OUTPUT:
输出满足条件的最小正m,没有解输出-1
分析:
方程组:m≡ri(mod ai)
假设m%a1=r1,m%a2=r2。那么我们将式子写开就变成了m=k1*a1+r1,m=k2*a2+r2。所以联立两个式子就可以得到k1*a1+r1=k2*a2+r2。那么移项可得a1*k1-a2*k2=(r2-r1)。得到这个式子后我们就可以利用扩展欧几里德算法求出k1,k2的值。
这里要保证k1为正数。当k1为负数时,k1不断的加上a2/gcd(a1,a2),k2同时就要减去a1/gcd(a1,a2)。求出正数的k1以后,就可以计算出m的值。这里先暂时记作m0。
我们知道这属于不定方程,所以x满足这两个模线性方程,x0+k*a1也满足两个模线性方程组,x0+k*a2也满足两个模线性方程。所以满足这两个模线性方程的x的通解为x=x0+k*lcm(a1,a2)。lcm(a1,a2)是a1和a2的最小公倍数。
我们将这个式子再次转换成模线性方程即得m≡m0(mod lcm(a1,a2))
这样就可以逐步迭代求出最终满足条件的x值。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
ll a[100010],r[100010];
void exgcd(ll a,ll b,ll& d,ll& x,ll& y){
if(b==0){
d=a;x=1;y=0;
}
else{
exgcd(b,a%b,d,y,x);y-=x*(a/b);
}
}
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++){
scanf("%d%d",&a[i],&r[i]);
}
ll lasta=a[0],lastr=r[0];
int ok=0;
for(int i=1;i<n;i++){
ll c=r[i]-lastr;
ll x,y,d;
exgcd(lasta,a[i],d,x,y);
if(c%d){
printf("-1\n");
ok=1;
break;
}
ll k=a[i]/d;
x*=c/d;
x=(x%k+k)%k;//这一步是很关键的,将特解缩小,避免中间结果溢出
lastr=x*lasta+lastr;
lasta=lasta*a[i]/d;//最小公倍数
lastr=(lastr%lasta+lasta)%lasta;
}
if(ok==0)
printf("%lld\n",(lastr%lasta+lasta)%lasta);
}
return 0;
}