POJ 2115 C Looooops (拓展的欧几里得) 拓展的欧几里得详解

本文介绍了一种利用扩展欧几里得算法解决模线性方程的方法,并通过具体实例展示了如何计算特定形式的C语言循环的执行次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 < 2k) modulo 2k.

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 < 2k) 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



题目大意:根据题意求出循环次数,若死循环输出FOEVER

思路:通过公式的变形可得C*x=(B-A)%2^k转化为模的线性方程


</pre><pre name="code" class="cpp">#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
LL x,y;
LL exgcd(LL a,LL n)
{
    LL t,d;
    if(!n)//已知方程a*x+b*y=gcd(a,b){  公式一  };这里n-->b
    {
        x=1;y=0;//当n==0,a*x=gcd(a,0)=a;所以x=1,y=0;
        return a;
    }//否则继续进行
    d=exgcd(n,a%n);//当再次递归时gcd(a,n)=gcd(n,a%n),将其带入公式1中变形可得
    //b*x1+(a%b)*y1==b*x1+(a-(a/b)*b)*y1==a*y1+b*(x1-(a/b)*y1),最终得到ax+by==a*y1+b*(x1-(a/b)*y1)
    t=x;//既可以得到x=y1(y),y1(y)=x1-(a/b)*y1
    x=y;
    y=t-a/n*y;
    return d;
}
int main()
{
    LL n,k,i,j,A,B,C,a,b,d;
    while(~scanf("%lld%lld%lld%lld",&A,&B,&C,&k))
    {
        if(A==0&&B==0&&C==0&&k==0)//已知拓展的欧几里得公式为ax+by=gcd(a,b);
            break;
        n=(1LL<<k);//若不再1后加LL会WA- -!
        a=C;b=B-A;//根据题意,可以推得C*x=(A-B)%2^k;此方程为模线性方程即a*x=b%n;
        d=exgcd(a,n);
        if(b%d)//模线性方程有解的充要条件是b%(gcd(a,n))==0
        {
            puts("FOREVER");
            continue;
        }//如果线性方程
        x=x*(b/d)%n;//方程a*x=b%n的最小解
        x=(x%(n/d)+(n/d))%(n/d);// x%(n/gcd(a,n))使解落到区间-n/gcd(a,n)~n/gcd(a,n),
        //再加上n/gcd(a,n)使解在区间0~2*n/gcd(a,n),
//再模上n/gcd(a,n),则得到最小整数解 
        printf("%lld\n",x);
    }
    return 0;
}


思路:扩展的欧几里得为ax+by=c   =>    一、ax+by=GCD(a,b)  ,  通过此题二、Cx+my=B-A,同时 我们还可以得到公式  Cx+A+y2^k=B   即Cx=(B-A)%2^k

我们可以吧b=(B-A);   通过ex扩展GCD公式求出x,注意这里的x不是公式二Cx+my=b中的x,若想由公式一中的推出公式二可以将公式一同时乘b/GCD(C,2^k);

这就是为什么x=x*(b/GCD()).还有x取得范围问题,(x0+k*(m/GCD()) ,y0+k*(C/GCD()));(T_T终于明白了!!!!!)



#include<map>
#include<queue>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
#include<algorithm>
#define LL __int64
#define inf 0x3f3f3f3f
const double PI=acos(-1.0);
using namespace std;

void ex(LL a,LL b,LL &d,LL &x,LL &y){
    if(!b){
        d=a,x=1,y=0;
    }
    else{
        ex(b,a%b,d,y,x); y-=x*(a/b);
    }
}
int main(){
    LL b,B,A,C,n,k,d,x,y;
    while(~scanf("%I64d%I64d%I64d%I64d",&A,&B,&C,&k)){
        if(!A&&!B&&!C&&!k) break;
        n=(1LL<<k);
        b=B-A;
        ex(C,n,d,x,y);
        if(b%d==0){
            x=x*(b/d)%n;
            x=(x%(n/d)+n/d)%(n/d);
            printf("%I64d\n",x);
        }
        else{
            puts("FOREVER");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值