(模板题)poj 2115 C Looooops(扩展欧几里得算法)

C Looooops
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 23790 Accepted: 6581

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

Source

提示

题意:

给出A,B,C(0<=A,B,C<2^k),k(1<=k<=32)。经过几次循环才能使A=B,用for循环写成:for(i=A;B!=i;i=i+C),如果数值超出2^k,那就像电脑从0开始计数,比如k=2,运行后A变成5,那么2^2<5,5转化为1。如果永远不能跳出循环输出"FOREVER"。

思路:

首先我们可以写出Cx=(B-A+2^k)%2^k

x=(B-A+2^k)%2^k/C

之后。。。还是不能得出答案

因为有不能跳出循环这一情况,需要用扩展欧几里得算法(来源于书):

形如ax+by=c的不定方程称为二元一次不定式方程。显然(1)a=0或b=0时,方程的解确定。(2)c不是gcd(a,b)的倍数时,方程无解。

因此,只考虑ab!=0且gcd(a,b)能整除c的情况。

求最大公约数的过程:

r0=r1*q1+r2

r1=r2*q2+r3

......

rk-1=rk*qk

由倒数第二个式子往回推演,将倒数第二个式子变形得到

gcd(a,b)=rn-1*x1+rn-2*y1

联立倒数第三个式子消去rn-1得到

gcd(a,b)=rn-2*x2+rn-3*y2

以此类推,一直得到

gcd(a,b)=a*x+b*y

如此便得出了ax+by=gcd(a,b)的解,那么c是gcd(a,b)的倍数时的解也就很明显了。

那么要怎么套呢?

刚推出的方程简化为Cx=(B-A)mod2^k,这里我们称x是关于2^k的乘法逆元,表达式等价与Cx+(2^k)*y=(B-A)。

之后就好做了吧。

扩展欧几里得详解

示例程序

Source Code

Problem: 2115		Code Length: 760B
Memory: 388K		Time: 0MS
Language: GCC		Result: Accepted
#include <stdio.h>
long long x,y;
long long f(long long a,long long b)
{
    long long t,d;
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    d=f(b,a%b);
    t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
int main()
{
    long long k,i,d,A,B,C,a,b,c;
    scanf("%lld %lld %lld %lld",&A,&B,&C,&k);
    while(A!=0||B!=0||C!=0||k!=0)
    {
        b=1;
        for(i=1;k>=i;i++)
        {
            b=b*2;
        }
        a=C;
        c=B-A;
        d=f(a,b);			//扩展欧几里得算法
        if(c%d!=0)
        {
            printf("FOREVER\n");
        }
        else
        {
            x=(x*c/d)%b;			//完全循环一次就够了,所以有c/d
            b=b/d;
            x=(x%b+b)%b;			//因为都是非负数所以要进行转化
            printf("%lld\n",x);
        }
        scanf("%lld %lld %lld %lld",&A,&B,&C,&k);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值