题意:给出a,b,c,k,求x使得(a + x * c) % (2^k) = b.
上述方程即:a + x * c ≡ b(mod (2^k))
左边的a需要化掉,因此两边同减a得到:
x * c ≡ b (mod (2 ^ k)) - a(这里有点滥用同余表示法的味道,望见谅)
右边如果直接(b - a)(mod (2 ^ k))显然可能出现负值,等号就不满足了,为此加上(2 ^k)的若干倍,这样结果就不变,加多少倍呢?加a倍显然是正确的,所以我们加a倍吧,这里随便搞...d当然,根据题意,有a < 2^k,因此加一倍足够了,写的时候没想那么多。。。
于是变成:
x * c ≡ (b - a + (2 ^ k) * a)(mod (2 ^ k))。
做一下变量代换,其实可以表示成:
a * x ≡ b (mod n)(注意,a, b和上边的a,b已经不同)
这就是线性同余方程了。
于是我们可以最后得到一个解系x = x0 + m * k(k ∈Z)。其中m = n / d可以唯一确定的。
此题我们理解一下题意,显然需要求出该解系中的最小非负整数解。求出上述所有解显然不靠谱,虽然至多有n / d个,但是在极限条件下这个数会相当大。我们用取余的小技巧:ans = (x0 % m + m) % m.
求解a * r + s * n = d的过程用扩展欧几里得算法即可。具体就不介绍了。
#include <cstdio>
using namespace std;
typedef long long ll;
ll extgcd(ll a, ll b, ll& x, ll& y) {
ll d = a;
if (b) {
d = extgcd(b, a % b, y, x);
y -= a / b * x;
} else {
x = 1;
y = 0;
}
return d;
}
int main() {
ll a, b, c, d, k, x, y;
while (~scanf(" %lld %lld %lld %lld", &a, &b, &c, &k) && k) {
ll n = 1LL << k;
if ((b - a + n) % (d = extgcd(c, n, x, y))) {
puts("FOREVER");
} else {
x = x * (b - a + n) / d;
printf("%lld\n", (x % (n / d) + n / d) % (n / d));
}
}
return 0;
}