【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);
  }
}
### POJ2891 Strange Way to Express Integers 的算法析 此问题的核心在于通过扩展中国剩定理来决一组模线性同余方程组。当模数不一定两两互质时,可以通过逐步合并的方式决问题。 #### 扩展中国剩定理的应用 对于两个方程 \( N \equiv r_1 \ (\text{mod} \ m_1) \) 和 \( N \equiv r_2 \ (\text{mod} \ m_2) \),我们需要找到满足这两个条件的最小非负整数。设 \( d = \gcd(m_1, m_2) \),则存在整数的前提是 \( (r_2 - r_1) \% d == 0 \)[^4]。如果该条件成立,则可通过扩展欧几里得算法求出特并进一步计算通。 具体步骤如下: 1. **初始化参数** 设初始状态为 \( M = m_1 \) 和 \( R = r_1 \)。 2. **逐对处理每一对模数和数** 对于当前的状态 \( M \) 和 \( R \),以及新的模数 \( m_i \) 和数 \( r_i \),我们尝试将其合并成一个新的关系: \[ xM + ym_i = r_i - R \] 如果 \( (r_i - R) \% gcd(M, m_i) != 0 \),说明无;否则继续下一步。 3. **利用扩展欧几里得算法求系数** 使用扩展欧几里得算法求上述不定方程的一组特 \( x_0 \) 和 \( y_0 \)。然后调整这些系数使得最终的结果落在有效范围内。 4. **更新全局变量** 更新后的模数为 \( lcm(M, m_i) \),而对应的数则是新算出来的值加上原来的偏移量。 5. **重复直到完成所有输入数据** 最后得到的 \( R \) 即为目标答案。 以下是基于 Python 实现的一个版本: ```python from math import gcd def ex_gcd(a, b): """扩展欧几里得算法""" if b == 0: return a, 1, 0 g, x, y = ex_gcd(b, a % b) return g, y, x - (a // b) * y def solve(): k = int(input()) mods = [] rems = [] for _ in range(k): mi, ri = map(int, input().split()) mods.append(mi) rems.append(ri) M = mods[0] R = rems[0] for i in range(1, len(mods)): Mi = mods[i] Ri = rems[i] g, p, q = ex_gcd(M, Mi) if (Ri - R) % g != 0: print(-1) # No solution exists. return tmp = ((Ri - R) // g) * p % (Mi // g) R += M * tmp M *= Mi // g while R < 0: R += M print(R % M) if __name__ == "__main__": T = int(input()) # Number of test cases results = [] for t in range(T): solve() ``` 以上程序实现了扩展中国剩定理的方法,并能够正确处理多组测试样例的情况。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值