测试地址:Strange Way to Express Integers
题目大意:用以下方法表示一个非负整数
M
:选取任意
做法:这道题需要使用扩展欧几里得来解一元线性同余方程组。
分析题目,实际上题目要求的就是这个同余方程组的最小非负整数解:
M≡ri(modai)(1≤i≤k)
我们知道当模数 ai 之间两两互质的话,就可以用中国剩余定理简便地得出答案,然而这题并没有这个限制。面对更一般的情况,我们要怎么办呢?
答案是,我们需要将同余方程一一合并。我们不妨先来探讨怎么把两个同余方程构成的同余方程组合并为一个等价的同余方程。我们知道这种同余方程实际上可以展开变成一个二元一次不定方程,即 M≡ri(modai) 可以表示成 M=ri+aiki ,那么我们将 i=1 和 i=2 的两个方程联立,得到: r1+a1k1=r2+a2k2 ,所以: a1k1−a2k2=r2−r1 。设 d=gcd(a1,a2) ,我们先用扩展欧几里得求出 a1x′−a2y′=d 的解,如果 d|(r2−r1) ,说明方程有解,则原方程中 k1 的一个解 x0=(x′×r2−r1d)moda2d ,通解为 k1=x0+k0×a2d 。将该式代入到方程 M=r1+a1k1 中,得到 M=r1+a1x0+k0×a1a2d ,那么原先的两个同余方程就合并为了以下方程:
M≡r1+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);
}
}