POJ 2891 Strange Way to Express Integers 非互质的中国剩余定理
2012-07-31 09:09 javaspring 阅读(182) 评论(0) 编辑 收藏 举报来源: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; }