题目描述
一个C语言的for循环:
for(int i=A;i!=B;i+=C)st;
其中i表示变量,A、B和C分别表示初值、终值和步长,st表示循环体。
要求计算当循环变量i运算在k位无符号整数体系下(运算值范围:0~2^k-1,此时,i+=c溢出后截断),循环体st会执行多少次?
输入格式
输入每组数据包括4个整数A,B,C和k。
输出格式
对于每组数据,输出循环次数,如果永远不能结束,输出FOREVER。
输入样例
1 3 2 4
1 5 2 4
1 2 4 3
0 0 0 0
输出样例
1
2
FOREVER
数据范围
1≤k≤32。
0≤A, B, C≤2^k。
思路:
题意很容易联想到取模运算
设 m = 1<<k;
则可列出 A + xC %m == B 如果 只需找到x的最小解即可
转换得 A + x * C + y * m == B
又得 x * C + y * m == B - A 利用exgcd解出x, y 即可
并且B - A 为 gcd(C, m)的整数倍时方程有解,
对该条件进行解释:
原方程化为:ax + kn = b (设k为某一整数)
那么如果a与n的最大公约数为d,那么ax + kn 必然可以提取一个d的因子,也就是说b必然有d这个因子,所以如果b%d!=0,说明b没有d这因子,与前面的结论相互矛盾,所以无解.
如果解出x, y 且(B - A) % gcd(C, m) == 0 ;
此时的x, y 是在 x*C + y * m == gcd(C, m) 解出的, 令 t = (B - A) / gcd(C, m)
而方程 x * C + y * m == B - A的解为 x1 = x * t , y1 = y * t;
又由线性同余方程的通解ax + by == gcd(a, b), x = x0 + k1(a/gcd(a, b)), y = y0 - k2(b/gcd(a, b)
所以本题中的x1最小解即为令 z = m / gcd(c, m) 则 Xmin = (x1 % z + z) % z,(防止为负数)
更多细节见于代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
/*
A + xC % K == B
A + xc + yk == B
xc + yk == B - A
如果 x * c + y * k == kd == B - A
x = x0 + k(a / gcd(a, b))
*/
ll exgcd(ll a, ll b, ll &x, ll &y) // 在求出x, y的同时求出gcd(a, b)
{// ax + by == gcd(a, b) = gcd(b, a % b) = bx + a%b * y = b*x + (a - a/b*b)*y = a*y - b*(x - a/b * y)
if(b == 0)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
ll a, b, c;
int k;
while(cin >> a >> b >> c >> k && k)
{
ll x, y;
ll z = 1ll<<k;
ll d = exgcd(c, z, x, y);
if((b - a) % d != 0)
{
cout << "FOREVER" << endl;
continue;
}
x *= (b - a) / d; // 因为之前的x是按照gcd(c, z)算出来的, 而gcd(c, z)不一定是等于b - a的, 所以要乘以其倍数
z /= d;
cout << (x % z + z) % z << endl; // 防止x是负数
}
return 0;
}