题目大意:
给出一组数 a b c k,问 a + mc = b(mod 2^k)的解m是多少,若有解,则输出最小正解的大小,若没有,输出FOREVER,表明a永远无法通过加c的方式得到b的值。
解题思路:
根据刚才列出的式子可以很容易的发现,此题可以直接使用扩展欧几里得算法求解,扩展欧几里得算法用于求解形如 ax+by=d=gcd(a,b)的方程,求得的解x和y满足x+y的和最小。
由欧几里得算法可知 gcd(a,b)=gcd(b,a%b)(证明略)
我们可以列出另一个方程:
bx1+(a%b)y2=d=gcd(b,a%b)=gcd(a,b)=ax1+by1
即:bx+(a-a/b*b)y=ax+by
展开可得: a * y2 + (x - a/b*y2)b = a * x1 + b * y1
由等式两边的对应关系可得:y2 = x1 , y1 = x - a/b * y2
由这个过程递推到gcd(a,b)= 1 将产生的 x 和 y 收集起来 即为这个二元一次方程的整数解集。
由上述推导,我们可以构造出一个式子:
x * C + y * 2^k = b - a = p * d = p * gcd( C,2^k)
带入扩展欧几里得算法解出系数即可,注意这里的系数还需要乘上系数,也就是 b-a 是 C,2^k 的最大公约数的正整数倍,所以还需要乘上系数。
题目中有一个坑点:扩展欧几里得算法求解出的系数是满足x+y最小的,而题目中要求我们求出的是 x 的最小值,所以需要在适当的范围内对x的值进行调整使得其成为最小的正整数解。
由于方程的解必须为整数,我们求得的解也为整数,所以应该在解的基础上减去解的分布周期, 由最初始的式子 a * x +b*y= d 可以发现,在 ax 减小的大小等于 by 增加的大小,这个值刚好是 a b 的最小公倍数,而 a b 的最小公倍数是 a b 的乘积除以最大公约数 d ,即(a * b / d)。这样我们就得到了周期,在调整的过程中要注意,方程求解会得到 x 小于 0 的解,因此只需对负数解的情况稍加判断就可以正确的AC此题了。
希望大家都能快速AC,并搞懂扩展欧几里得内部的证明和原理!
附上代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long int lli;
lli mi[35];
void init()
{
mi[0]=1;
for(int i=1;i<=32;i++)
mi[i]=2*mi[i-1];
}
lli exgcd(lli a,lli b,lli &x,lli &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
lli ans=exgcd(b,a%b,x,y);
lli temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
int main()
{
lli A,B,C,k,x,y;
init();
lli num,key,sum,temp;
while(~scanf("%lld%lld%lld%lld",&A,&B,&C,&k),(A+B+C+k))
{
num=B-A;
key=exgcd(C,mi[k],x,y);
if(num<0)
num+=mi[k];
if(num%key!=0)
printf("FOREVER\n");
else
{
temp=mi[k]/key;
sum=x*num /key;
sum %= temp;
while(sum < 0)
sum += temp;
printf("%lld\n",sum);
}
}
return 0;
}