POJ 2115 Looooops (扩展欧几里得+调整解)

题目大意

给出一组数 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值