【POJ2891】Strange Way to Express Integers-解一元线性同余方程组

测试地址:Strange Way to Express Integers
题目大意:用以下方法表示一个非负整数M:选取任意k个不同的正整数a1,a2,...,ak,求出r1=M%a1,r2=M%a2,...,rk=M%ak,可以得到k组数对(ai,ri)。现在给你这k组数对,请求出满足条件的最小的非负整数M
做法:这道题需要使用扩展欧几里得来解一元线性同余方程组。
分析题目,实际上题目要求的就是这个同余方程组的最小非负整数解:

Mri(modai)(1ik)

我们知道当模数ai之间两两互质的话,就可以用中国剩余定理简便地得出答案,然而这题并没有这个限制。面对更一般的情况,我们要怎么办呢?
答案是,我们需要将同余方程一一合并。我们不妨先来探讨怎么把两个同余方程构成的同余方程组合并为一个等价的同余方程。我们知道这种同余方程实际上可以展开变成一个二元一次不定方程,即Mri(modai)可以表示成M=ri+aiki,那么我们将i=1i=2的两个方程联立,得到:r1+a1k1=r2+a2k2,所以:a1k1a2k2=r2r1。设d=gcd(a1,a2),我们先用扩展欧几里得求出a1xa2y=d的解,如果d|(r2r1),说明方程有解,则原方程中k1的一个解x0=(x×r2r1d)moda2d,通解为k1=x0+k0×a2d。将该式代入到方程M=r1+a1k1中,得到M=r1+a1x0+k0×a1a2d,那么原先的两个同余方程就合并为了以下方程:
Mr1+a1x0(moda1a2d)

这样一步步合并下去即可合并整个方程组,然后就可以得出解了。因为数字较大,所以在一些地方要防止溢出,其次就是要注意计算各个值的顺序,然后我们就完美地解决了这道题。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n;
ll a1,a2,r1,r2,x0,y0,d;

void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
  ll x0,y0,x1,y1;
  x0=1,y0=0;
  x1=0,y1=1;
  while(a%b)
  {
    ll tmp,q;
    q=a/b;
    tmp=x0,x0=x1,x1=tmp-q*x1;
    tmp=y0,y0=y1,y1=tmp-q*y1;
    tmp=a,a=b,b=tmp%b;
  }
  d=b,x=x1,y=y1;
}

int main()
{
  while(scanf("%d",&n)!=EOF)
  {
    scanf("%lld%lld",&a1,&r1);
    bool flag=1;
    for(int i=1;i<n;i++)
    {
      scanf("%lld%lld",&a2,&r2);
      if (!flag) continue;
      exgcd(a1,a2,d,x0,y0);
      if ((r2-r1)%d) {flag=0;r1=-1;continue;}
      ll t=a2/d;
      x0=(x0*((r2-r1)/d)%t+t)%t; //防止结果是负数
      r1=r1+a1*x0;
      a1=a1*(a2/d);
    }
    printf("%lld\n",r1);
  }
}
posted @ 2017-05-26 11:42  Maxwei_wzj  阅读(115)  评论(0编辑  收藏  举报