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

来源:互联网 发布:阿里丁丁 mac 编辑:程序博客网 时间:2024/06/14 15:12

题目大意

给出一组数 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
原创粉丝点击