来源:http://poj.org/problem?id=2891
题意:有一个数x,x = r[i] (mod a[i]),其中a[i] 和 a[j] 不一定互质,求x的最小值。
思路:很容易看到这题和中国剩余定理是有联系的。因为倘若任意的a[i] 和 a[j] 互质,则满足中国剩余定理。中国剩余定理还是好理解的,这里就不多说了,主要说一下不互质情况下如何转化为互质下的中国剩余定理。
转化主要是用了合并的思想。设 x = r1 (mod a1) x = r2 (moda2),则易知 x = k1 * a1 + r1,x = k2 * a2 + r2,因此k1 * a1 + r1 = k2 * a2 + r2,化简后可得,k1 * a1= (r2 – r1) + k2 * a2,因为k2 * a2 % a2 = 0,所以k1 * a1 = (r2 – r1) (mod a2),因此我们很容易判断有解无解的条件,若gcd(a1,a2)| (r2 – r1),则有解,否则无解。设d = gcd(a1,a2),c = (r2 – r1),则a1*k1/d = (r2 –r1)/d (mod a2/d),通过化简后,我们可得k1 = c/d * (a1/d)^(-1) (mod a2 / d),其中(a1/d)^(-1)代表(a1/d)对于(a2/d)的乘法逆元。这点解释一下,因为是对(a2/d)取余,所以是对(a2/d)的乘法逆元很好理解,在说为什么是乘法逆元。x乘法逆元的概念就是y,使得x*y % n = 1,由于a1/d 和a1/d 的倒数的乘积为1,故他们的乘积对n取余为1,满足乘法逆元。因此(a1/d)^(-1)是关于(a2 / d)的乘法逆元。由于c/d * (a1/d)^(-1)可以算出来,即已知。所以我们令K = c/d * (a1/d)^(-1),即k1 = K ( mod a2/d),所以k1 = y * a2/d + K,将此式代入 x = k1 * a1 + r1,得x = (a1 * K + r1) (mod a1 * a2 / d),我们令r = (a1 * K +r1),a = a1 * a2 / d,因此我们得到一个新的式子,即 x = r (mod a),注意这个式子和开始我们所写的式子格式是一样的。也就是说,两个式子能够合并成一个,然后继续合并,易知最后肯定会合并为一个式子。我们设最后所合并的式子为x = r (mod a),求x的最小值,即x = (r%a + a) % a就是最终答案。至此,这道题已经解决。
代码:#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
const int N = 1010;
typedef long long ll;
ll gcd(ll a,ll b){
if(b == 0)
return a;
return gcd(b,a%b);
}
void extend_Eulid(ll a,ll n,ll &x,ll &y){
if(n == 0){
x = 1;
y = 0;
return;
}
extend_Eulid(n,a%n,x,y);
ll temp = x;
x = y;
y = temp - a/n * y;
}
ll inv(ll a,ll n){
ll d = gcd(a,n);
if(d != 1)
return -1;
ll x,y;
extend_Eulid(a,n,x,y);
return (x % n + n) % n;
}
bool merge(ll a1,ll r1,ll a2,ll r2,ll &a3,ll &r3){
ll d = gcd(a1,a2);
ll c = r2 - r1;
if(c % d)
return false;
c = (c % a2 + a2) % a2;
a1 /= d;
a2 /= d;
c /= d;
c *= inv(a1,a2);
c %= a2;
c *= (a1*d);
c += r1;
a3 = a1 * a2 * d;
r3 = (c % a3 + a3) % a3;
return true;
}
ll China_Remain(ll n,ll a[N],ll r[N]){
ll a1 = a[1],r1 = r[1];
for(ll i = 2;i <= n;++i){
ll a2,r2,a3,r3;
a2 = a[i];
r2 = r[i];
// printf("a1 = %lld r1 = %lld\n",a1,r1);
if(!merge(a1,r1,a2,r2,a3,r3)){
//printf("i == %d\n",i);
return -1;
}
a1 = a3;
r1 = r3;
}
return (r1 % a1 + a1) % a1;
}
int main(){
//freopen("1.txt","r",stdin);
ll n,a[N],r[N];
while(scanf("%lld",&n) != EOF){
CLR(a,0);
CLR(r,0);
for(ll i = 1;i <= n;++i)
scanf("%lld%lld",&a[i],&r[i]);
ll ans = China_Remain(n,a,r);
printf("%lld\n",ans);
}
return 0;
}