http://poj.org/problem?id=2115
通过这道题有学习了一些新的数论知识,感觉很充实。哈哈。
做这道题首先要理解:同于定理:http://baike.baidu.com/view/1490645.htm?fromTaglist
这里主要运用了(2)
同余有三种说法都是等价的,分别为:
(1) a和b是模d同余的. a=b(mod d)这里的=是三道杠的。
(2) 存在某个整数n,使得a=b+nd .
(3) d整除a-b.
第二就是扩展欧几里德:
日华的博客里面写的扩展欧几里德不错:给个链接:http://starry314.blog.163.com/blog/static/19231833820121724628448/
题目的意思就是求(A + C*x)=B(mod 2^k)注意这里的等号是同于的等号(不知道怎么大三个杠的囧)
首先根据同于性质(2)得: A + C*x + 2^k*y = B ----> C*x + 2^k*y = (B - A + 2^k)%2^K
另 a = C b = 2^k p = (B - A + 2^k)%2^k ---> a*x + b*y = p;这就转化成扩展欧几里德求解线性方程的问题看上边博客。
这里自己补充一点:
当我们运行完exp_gcd函数时,我们得到的x1,y1是a*x1 + b*y1 = gcd(a,b) = d;的x,y的解,而我们通常要求的是a*x + b*y = p的解
这里有个推导过程:
a*x1 + b*y1 = d ---> (a/d)*x1 + (b/d)*y1 = 1;............(1);
a*x + b*y = p -----> (a/p)x + (b/p)*y = 1;..........(2);
由(1)(2)得:x = (p/d)*x1; y = (p/d)*y1; 得到x,y后再利用 a(x + n*b) + b(y - n*a) = p 来对x,y进行缩小放大。如果要得到最小的x则直接由 x = (x%b + b)%b得到即可
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define maxn 507 #define LL __int64 using namespace std; LL Pow(int a,int k) { LL sum = 1; for (int i = 1; i <= k; ++i) sum *= a; return sum; } LL exp_gcd(LL a,LL b,LL &x,LL &y) { if (b == 0) { x = 1; y =0; return a; } else { LL d = exp_gcd(b,a%b,x,y); LL tmp = x; x = y; y = (tmp - (a/b)*y); return d; } } int main() { LL A,B,C,a,b,p,x,y; int k; while (~scanf("%I64d%I64d%I64d%d",&A,&B,&C,&k)) { if (!A && !B && !C && !k) break; a = C; b = Pow(2,k); p = (B - A + b)%b; LL d = exp_gcd(a,b,x,y); if (p%d != 0) { printf("FOREVER\n"); } else { /* 这里自己开始没理解好,我们求出来的x,y是a*x + b*y = gcd(a,b)的解, 而我们要的则是a*x + b*y = p的解,所以要转化一下 */ p /= d; b /= d; a /= d; x *= p; x = (x%b + b)%b; printf("%I64d\n",x); } } return 0; }