【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=1 i=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);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值