poj2115 C Looooops(扩展欧几里得)

C Looooops
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 23143 Accepted: 6357

Description

A Compiler Mystery: We are given a C-language style for  loop (环) of type 
for (variable = A; variable != B; variable += C)
statement;

I.e., a loop which starts by setting  variable (变量的) to value A and while variable is not equal to B, repeats  statement (声明) followed by increasing the variable by C. We want to know how many times does the statement get  executed (实行) for particular values of A, B and C,  assuming (承担) that all arithmetics is  calculated (计算) in a k-bit unsigned  integer (整数) type (with values 0 <= x < 2 k) modulo 2 k

Input

The  input (投入) consists of several  instances (实例). Each instance is described by a single line with four  integers (整数) A, B, C, k separated by a single space. The integer k (1 <= k <= 32) is the number of bits of the control  variable (变量) of the  loop (环) and A, B, C (0 <= A, B, C < 2 k) are the  parameters (参数) of the loop. 

The input is finished by a line containing four zeros. 

Output

The  output (输出) consists of several lines corresponding to the instances on the input. The i-th line contains either the number of  executions (执行) of the  statement (声明) in the i-th instance (a single integer number) or the word FOREVER if the loop does not  terminate (终止)

Sample Input

3 3 2 16
3 7 2 16
7 3 2 16
3 4 2 16
0 0 0 0

Sample Output

0
2
32766
FOREVER

题意:告诉你一个循环for(i=a;i!=b;i+=c)问你在2^k方内至少循环多少次才能退出,如果不能退出就输出“FOREVER”,也就是说i不断累加的过程中需要mod2^k。

输入:a,b,c,k我们要解的就是a+cx=b(modk)(下面的2^k我都用k来代替)。

要解这个x我们先来看一下扩展欧几里得算法:

书本上的内容:先用一个引例

直线上的点。求直线ax+by+c=0上有多少个整数点(x,y)满足x属于[x1,x2],y属于[y1,y2]。

下面是扩展欧几里得算法的程序:

void exgcd(int a,int b,int &d,int &x,int &y)
{
    if(!b)
    {
        d=a;
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,d,y,x);
        y-=a/b*x;
    }
}
具体的证明方法可以自行百度,d最后的结果就是gcd(a,b)。它在递归调用时,x和y的顺序变了,而边界也是不难得出的:gcd(a,0)=1*a-0*0=a。这样,唯一需要记忆的就是y-=x*(a/b),看不太懂的同学自行百度好好理解一下。

上面求出了ax+by=gcd(a,b)的一组解(x1,y1),那么其他的解呢,任取另外一组解(x2,y2),则ax1+by1=ax2+by2(它们都等于gcd(a,b)),变形得

a(x1-x2)=b(y2-y1)。假设gcd(a,b)=g,方程左右两边同时除以g,得a1(x1-x2)=b1(y2-y1),其中a1=a/g,b1=b/g。注意,此时a1和b1互素,因次x1-x2一定是b的整数倍。设它为kb1,计算得y2-y1=ka1。注意,上面的推导过程并没有用到(ax+by的右边是什么),因此得出以下结论:

1.设a,b,c为任意整数。若方程ax+by=c的一组解为(x0,y0),则它的任意整数解都可以写成(x0+kb1,y0-ka1)其中a1=a/gcd(a,b),b1=b/gcd(a,b),k取任意整数。

有了这个结论,移项得ax+by=-c,然后求出一组解即可。例如:

6x+15y=9.根据扩展欧几里得算法,已经得到了6×(-2)+15×1=3,两边同时乘以3得6×(-6)+15×3=9,即x=-6,y=3时6x+15y=9。

6x+15y=8,两边除以3得2x+5y=8/3。左边是整数而右边显然不是,显然这个方程是没有整数解的。综合起来有下面的结论:

2.设a,b,c为任意整数,g=gcd(a,b),方程ax+by=g的一组解是(x0,y0),则当c是g的倍数时ax+by=c的一组解是(x0*c/g,y0*c/g);当c不是g的整数倍时无整数解。

这样这个问题就好办了,我们把上述的方程a+cx=b(modk)化为cx+ky=b-a求出一组解来即可。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
typedef long long ll;
using namespace std;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)//扩展欧几里得
{
    if(!b)
    {
        d=a;
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,d,y,x);
        y-=a/b*x;
    }
}

int main()
{
    ll a,b,c,k;

    while(~scanf("%lld%lld%lld%lld",&a,&b,&c,&k))
    {
        if(!a&&!b&&!c&&!k)break;
        k=1ll<<k;//这里注意形式,我贡献了10几发哇QAQ注意用1LL<<1
        ll ans,x,y,t=b-a,shang;//t就是方程右边的数,shang用来记录b1,(在这里就是x的循环步数)
        exgcd(c,k,ans,x,y);
        if(t%ans==0)//如果存在整数解
        {
            shang=k/ans; //这里求的商是(上面所说的b1)
            x=(x*(t/ans))%shang;//这里求出的x是方程的最小解
            x=(x+shang)%shang;//这里因为x可能是小于0的,所以要特殊做一下处理
            printf("%lld\n",x);
        }
        else printf("FOREVER\n");
    }
    return 0;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值