POJ 2891 扩展欧几里德

这题也搞了好久,现在还有点没有全部理解,还请高人指点……

这道题不能用中国剩余定理,因为按照剩余定理的要求,a1,a2,a3....an要两两互素,而此题不符合。
此题的方法是用扩展欧几里德,逐渐合并。
例如有前两对数,a1,r1,a2,r2;
设所求的数为m,则有:a1*x+r1=m=a2*y+r2
联立得:a1*x-a2*y=r2-r1;
设r=r2-r2;
求出gcd(a1,a2)判断是否符合:r%gcd!=0;
 不符合就不用往下算了,输出-1;
符合就继续:
a1*X-a2*Y=gcd;

temp=a2/gcd;  //这个就不太理解了
x=(X*r/gcd%temp+temp)%temp;   //这个是防止为负数的情况
r1=a1*x+r1;   //这个就是a1*x+r1=m

a1=a1*a2/gcd;   //这个也不太理解了
此时两个方程就和并成了一个。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    ll t=(!b?a:gcd(b,a%b));   //记忆递归省点时间
    return t;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b) {x=1;y=0;return;}
    exgcd(b,a%b,y,x);  //这里用记忆化递归时间还是一样的,所以不用
    y-=a/b*x;
}
int main()
{
    ll i,k,a1,r1,a2,r2,c,x,y,l,t;
    while(cin>>k)
    {
        int flag=0;
        cin>>a1>>r1;
        for(i=1;i<k;i++)
        {
            cin>>a2>>r2;
            if(flag) continue;
            c=r2-r1;
            l=gcd(a1,a2);
            if(c%l!=0) {flag=1;continue;}
            exgcd(a1,a2,x,y);
            t=a2/l;
            x=((c/l*x)%t+t)%t;
            r1+=a1*x;
            a1*=t;
        }
        if(flag) cout<<-1<<endl;
        else cout<<r1<<endl;
    }
    return 0;
}


你可能感兴趣的:(POJ 2891 扩展欧几里德)