POJ 2891 Strange Way to Express Integers

题目:

对于正整数a1, a2, …, ak,用m模得到对应余数 ri

现在已知所有的整数对 (ai, ri) ,求对应的m

INPUT:

多组测试数据,每一组,第一行是k,后面k行每行是一个整数对 (ai, ri)

OUTPUT:

输出满足条件的最小正m,没有解输出-1

分析:

方程组:m≡ri(mod ai)

假设m%a1=r1,m%a2=r2。那么我们将式子写开就变成了m=k1*a1+r1,m=k2*a2+r2。所以联立两个式子就可以得到k1*a1+r1=k2*a2+r2。那么移项可得a1*k1-a2*k2=(r2-r1)。得到这个式子后我们就可以利用扩展欧几里德算法求出k1,k2的值。

这里要保证k1为正数。当k1为负数时,k1不断的加上a2/gcd(a1,a2),k2同时就要减去a1/gcd(a1,a2)。求出正数的k1以后,就可以计算出m的值。这里先暂时记作m0。

我们知道这属于不定方程,所以x满足这两个模线性方程,x0+k*a1也满足两个模线性方程组,x0+k*a2也满足两个模线性方程。所以满足这两个模线性方程的x的通解为x=x0+k*lcm(a1,a2)。lcm(a1,a2)是a1和a2的最小公倍数。

我们将这个式子再次转换成模线性方程即得m≡m0(mod lcm(a1,a2))

这样就可以逐步迭代求出最终满足条件的x值。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
ll a[100010],r[100010];
void exgcd(ll a,ll b,ll& d,ll& x,ll& y){
	if(b==0){
		d=a;x=1;y=0;
	}
	else{
		exgcd(b,a%b,d,y,x);y-=x*(a/b);
	}
}
int main(){
	int n;
	while(~scanf("%d",&n)){
		for(int i=0;i<n;i++){
			scanf("%d%d",&a[i],&r[i]);
		}
		ll lasta=a[0],lastr=r[0];
		int ok=0;
		for(int i=1;i<n;i++){
			ll c=r[i]-lastr;
			ll x,y,d;
			exgcd(lasta,a[i],d,x,y);
			if(c%d){
				printf("-1\n");
				ok=1;
				break;
			}
			ll k=a[i]/d;
			x*=c/d;
			x=(x%k+k)%k;//这一步是很关键的,将特解缩小,避免中间结果溢出 
			lastr=x*lasta+lastr;
			lasta=lasta*a[i]/d;//最小公倍数
			lastr=(lastr%lasta+lasta)%lasta;
		}
		if(ok==0)
		printf("%lld\n",(lastr%lasta+lasta)%lasta);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41333528/article/details/80776779