关于拓展欧几里德,别人已经写的很好的了,我也自己写一下,方便以后自己复习。
其最基础的思想就是 gcd(a,b)=gcd(b,a mod b),其中 gcd的意思是求最大公约数。。
拓展欧几里德是用来求 x,y 是的a*x+b*y=gcd(a,b),---我们这样想 对于a'=b, b'=a%b=a-(a/b*b),
a*x+b*y=gcd(a,b)
a'*x0+b'*y0=gcd(a',b')=gcd(a,b)=a*x+b*y
b*x0+(a-a/b*b)y0=a*x+b*y
a*y0+b*(x0-a/b*y0)=a*x+b*y
所以: x=y0 ; y=x0-a/b*y0;
所以 拓展欧几里德的函数为
{
if(t==0)
{
x=1;y=0
return s;
}
else
{
int i=extended_euclid(t,s%t);
int temp=x;
x=y;
y=temp-(s/t*y);
}
return i;
}
我们可以先求出一组x0,y0 是的a*x0+b*y0=(a,b)
然后 等号左右同时除以(a,b) 得: a*x0/(a,b)+b*y0/(a,b)=1
接着等号左右同时乘以 n 就得到 : a*x0/(a,b)*n + b*y0/(a,b)*n=n
所以 得到: x=x0/(a,b)*n y=y0/(a,b)*n 为方程的一组解了。
若 (a,b)=1 且 x0,y0,为方程 a*x+b*y=n的一组解 ,那么 x=x0+b*t ,y=y0-a*t (t为任意整数)都是方程的解。
而往往题目中要求求最小的解,那么我们就可以将一个特解x,t=b/(a,b),x=(x%t+t)%t;就可以了。
poj 2115
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 10534 | Accepted: 2475 |
Description
for (variable = A; variable != B; variable += C) statement;
Input
Output
Sample Input
3 3 2 16 3 7 2 16 7 3 2 16 3 4 2 16 0 0 0 0
Sample Output
0 2 32766 FOREVER
分析:题目给出 a,b,c,k,且1<=a,b,c<=2^k; 求最小的x ,使得 ( a+c*x ) mod 2^k = b。
喵呜的推导: a+c*x=b mod 2^k
即: a+c*x= b (mod 2^k)
即: a+c*x-b |2^k
所以存在t 使得 : t*2^k =a+c*x-b;
所以: t*2^k-c*x=a-b;
然后用 拓展欧几里德的思想做即可。。。
代码:
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
__int64 n,x,y;
__int64 extended_euclid(__int64 s,__int64 t) //拓展欧几里德算法
{
__int64 i,temp;
if(t==0)
{
x=1;
y=0;
return s;
}
else
{
i=extended_euclid(t,s%t);
temp=x;
x=y;
y=temp-(s/t*x);
}
return i;
}
int main()
{
__int64 a,b,c,i,j,e,m,k,d;
while(scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&k))
{
if(a==0 && b==0 && c==0 && k==0)
break;
d=(__int64)1<<k; //超过30位的要定义1为 64 位,看discuss里面才知道的。。。
e=extended_euclid(c,d);
if((b-a)%e) printf("FOREVER\n");
else
{
a=x*(b-a)/e%d+d; //公式推导。
printf("%lld\n",a%(d/e)); //求最小的那个 要 mod (d/e)
}
}
return 0;
}