poj1061 青蛙的约会 && poj 2115 C Looooops<扩展欧几里得>
链接 :http://poj.org/problem?id=1061 青蛙的约会
http://poj.org/problem?id=2115 C Looooops
首先我们先讨论欧几里得算法 ( gcd ):
gcd( a, b )即求两个数的最大公约数
递归算法:
int gcd( int a, int b )
{
return b==0?a:gcd( b, a%b ); //gcd(a,b) = gcd(a%b,b),这个递归一次以后就终止了无法保证a b可以继续减小,所以把 b 和 a%b交换顺序。
}
非递归算法:
int gcd( int a, int b )
{
if( b==0 )return 0;
while(b)
{
int t=a%b;
a=b;
b=t;
}
return a;
}
现在我们讨论算法的正确性,即证明gcd(a,b)==gcd(b,a%b),我们只要证明gcd(a,b)==gcd(a-b,b)即可,因为可以由此逐步扩展为gcd(a,b) == gcd(a-k*b,b),而 gcd(a-k*b,b)==gcd(a%b,b)。
因为a,b的公约数必然是a-b,b的公约数故 gcd(a,b) <= gcd(a-b,b);另a-b b的公约数也必然是a b的公约数,gcd(a,b) >= gcd(a-b,b).所以gcd(a,b) == gcd(a-b,b)。
再说扩展欧几里得:
扩展欧几里德算法是用来求解a*x+b*y==gcd(a,b)这样的方程的。同样利用gcd(a,b)==gcd(b,a%b)把a*x+b*y==gcd( a, b )转化为b*x'+(a%b)*y'==gcd( b, a%b );
根据递归的思想,假设现在我们已经求出了x' y',剩下的关键就是如何用x' y'求出x y.我们观察gcd(b,a%b) = b*x'+(a%b)*y',只要把右边重新写成 a*x+b*y 的形式就行了,所以需要对b*x'+(a%b)*y'进行变形,因为a%b == a-a/b*b,故b*x'+(a%b)y' = b*x'+(a-a/b*b)y' == a*y' + b*(x'-a/b*y') .
这样便可得出 x = y' y = x'-a/b*y'。
所以扩展gcd的递归算法为
LL exgcd( LL a, LL b, LL &x, LL &y )
{
LL d, t;
if( b==0 )
{
x=1, y=0;
return a;
}
d=exgcd( b, a%b, x, y );
t=x, x=y, y=t-a/b*y;
return d; // 返回gcd( a, b );
}
这样我们就得到了方程的解 :
x==x0+b*t; // 特解+通解
y==y0+a*t;
然后再看一般形式 a*x+b*y==c;
当且仅当 c%gcd( a,b )==0时方程才有解。
a*x+b*y==c的求解可以先求出a*x+b*y=gcd(a,b),然后将x y扩大c/gcd(a,b)倍就可以了。
1 /* 2 3 poj1061 4 5 */ 6 #include <stdio.h> 7 typedef long long LL; 8 LL gcd( LL a, LL b ) 9 { 10 return b==0?a:gcd( b, a%b ); 11 } 12 void exgcd( LL a, LL b, LL &x, LL &y ) 13 { 14 if( b==0 ) 15 { 16 x=1, y=0; 17 return ; 18 } 19 exgcd( b, a%b, x, y ); 20 LL t=x; 21 x=y; 22 y=t-a/b*y; 23 } 24 25 int main( ) 26 { 27 LL x, y, m, n, l; 28 LL a, b, c, k1, k2, r; 29 while( scanf( "%lld%lld%lld%lld%lld", &x, &y, &m, &n, &l )!= EOF ) 30 { 31 a=n-m, c=x-y; 32 r=gcd( a, l ); 33 if( c%r ) 34 { 35 puts( "Impossible" ); 36 continue; 37 } 38 a/=r, l/=r, c/=r ; 39 exgcd( a, l, k1, k2 ); 40 LL t=c*k1/l; 41 k1=c*k1-t*l; 42 if( k1<0 ) 43 k1 += l; 44 printf( "%lld\n", k1 ); 45 } 46 return 0; 47 }
1 /* 2 3 poj2115 4 5 */ 6 #include <stdio.h> 7 typedef long long LL; 8 LL exgcd( LL a, LL b, LL &x, LL &y ) 9 { 10 LL d, t; 11 if( b==0 ) 12 { 13 x=1, y=0; 14 return a; 15 } 16 d=exgcd( b, a%b, x, y ); 17 t=x, x=y, y=t-a/b*y; 18 return d; 19 } 20 21 // (a+c*m)%2^k=b ==> c*m-n*2^k=b-a; 22 int main( ) 23 { 24 LL A,B,C,k, a, b, c, x, y, n; 25 while(scanf("%lld %lld %Illd %lld",&A,&B,&C,&k)) 26 { 27 if(!A && !B && !C && !k) 28 break; 29 30 a=C, b=B-A, n=(LL)1<<k; //2^k 31 LL d=exgcd(a,n,x,y); //求a,n的最大公约数d=gcd(a,n)和方程d=ax+by的系数x、y 32 if(b%d!=0) //方程 ax=b(mod n) 无解 33 puts("FOREVER"); 34 else 35 { 36 x=(x*(b/d))%n; //方程ax=b(mod n)的最小解 37 x=(x%(n/d)+n/d)%(n/d); //方程ax=b(mod n)的最整数小解 38 printf("%lld\n",x); 39 } 40 } 41 return 0; 42 }