题目链接:http://poj.org/problem?id=2115
更多介绍:http://blog.csdn.net/lhfight/article/details/7755994
/*
* 题意不难理解,只是利用了 k位存储系统 的数据特性进行循环。
*例如int型是16位的,那么int能保存2^16个数据,即最大数为65535(本题默认为无符号),
*当循环使得i超过65535时,则i会返回0重新开始计数
*如i=65534,当i+=3时,i=1
*其实就是 i=(65534+3)%(2^16)=1
*有了这些思想,设对于某组数据要循环x次结束,那么本题就很容易得到方程:
*x=[(B-A+2^k)%2^k] /C
*即 Cx=(B-A)(mod 2^k) 此方程为 模线性方程,本题就是求X的值。
*令a=C
*b=B-A
*n=2^k
*那么原模线性方程变形为:
*ax=b (mod n)
*该方程有解的充要条件为 gcd(a,n) | b ,即 b% gcd(a,n)==0
*扩展的欧几里德
*令d=gcd(a,n)
*有该方程的 最小整数解为 x = e (mod n/d)
*其中e = [x0 mod(n/d) + n/d] mod (n/d) ,x0为方程的最小解
*那么原题就是要计算b% gcd(a,n)是否为0,若为0则计算最小整数解,否则输出FOREVER
*当有解时,关键在于计算最大公约数 d=gcd(a,n) 与 最小解x0
*参考《算法导论》,引入欧几里得扩展方程 d=ax+by ,
*通过EXTENDED_EUCLID算法(P571)求得d、x、y值,其中返回的x就是最小解x0,求d的原理是辗转相除法(欧几里德算法)
*再通过x0计算x值。注意x0可能为负,因此要先 + n/d 再模n/d。
*注意:计算n=2^k时,用位运算是最快的,1<<k (1左移k位)就是2^k
* */
import java.util.*;
public class PKU2115 {
static long x,y;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long A, B, C, K;
while (sc.hasNext()) {
A = sc.nextLong();
B = sc.nextLong();
C = sc.nextLong();
K = sc.nextLong();
if (A == 0 && B == 0 && C == 0 && K == 0)
break;
long n = 1L << K;// 移位运算,n=2^k
long a = C;
long b = B - A;
x = y = 0;
long gcd = extend_GCD(a, n);
//System.out.println(gcd);
if (b % gcd != 0)
System.out.println("FOREVER");// 方程 ax=b(mod n) 无解
else {
x = (x * (b / gcd)) % n; // 方程ax=b(mod n)的最小解
x = (x % (n / gcd) + n / gcd) % (n / gcd); // 方程ax=b(mod n)的最小整数小解
System.out.println(x);
}
}
}
private static long extend_GCD(long a, long b) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long ret = extend_GCD(b, a % b);
long t = x;
x = y;
y = t - a / b * y;
return ret;
}
}