题目:
对于正整数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; }