poj 2891 线性同余方程组,扩展欧几里得

题目链接:https://www.acwing.com/problem/content/206/

给定2n个整数a1,a2,…,an和m1,m2,…,mn,求一个最小的非负整数x,

满足∀i∈[1,n],x≡mi(mod ai)。

输入格式

第1行包含整数n。

第2..n行:每i+1行包含两个整数ai和mi,数之间用空格隔开。

输出格式

输出最小非负整数x,如果x不存在,则输出-1。
如果存在x,则数据保证x一定在64位整数范围内。

数据范围

1≤ai≤2^31−1,
0≤mi<ai
1≤n≤25

输入样例:

2
8 7
11 9

输出样例:

31

题解:

参考李煜东大佬的算法竞赛进阶指南。

       本题中的mi不一定两两互质,中国剩余定理不再适用。可以考虑使用数学归纳法,假设已经求出了前k-1个方程构成的方程组的一个解x。记m=lcm(m1,m2,,,,mk-1),则x+i*m 是k-1个方程的通解。

       考虑第k个方程,求出一个整数t,使得x+t*m≡ak(mod mk)。该方程等价于m*t≡ak-x(mod mk),其中t是未知量。这就是一个线性同余方程,可以使用扩展欧几里得算法判断是否有解,并求出它的解。若有解,则x'=x+t*m就是前k个方程构成的方程组的一个解。

     综上所述,使用n次扩展欧几里得算法,就求出了整个方程的解 。

  扩展欧几里得算法能算出ax+by=gcd(a,b)的一个特解,对于ax+by=c.  设 d=gcd(a,b). 如果 c%d==0 有解

通解是x’=c/d * x+k*(b/d)      另t=b/d,最小正整数解为x=(x*c/d%t+t)%t;

代码:

   

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50;
ll a[maxn],m[maxn];
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b) {x=1,y=0;return a;}
    ll d=exgcd(b,a%b,x,y);
    ll z=x;x=y;y=z-y*(a/b);
    return d;
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i]>>m[i];
     ll d,x,y;
     bool flag=true;
     for(int i=1;i<n;i++){
        ll c=m[i]-m[i-1];
        d=exgcd(a[i-1],a[i],x,y);
        if(c%d!=0){
            flag=false;
            break;
        }
        ll t=a[i]/d;
        x=(x*(c/d)%t+t)%t;
        m[i]=a[i-1]*x+m[i-1];
        a[i]=a[i-1]*(a[i]/d);
     }
     if(!flag) puts("-1");
     else printf("%lld\n",m[n-1]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值