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

来源:互联网 发布:啄木鸟审计软件 编辑:程序博客网 时间:2024/06/06 04:10

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 163 7 2 167 3 2 163 4 2 160 0 0 0

Sample Output

0232766FOREVER



题目大意:根据题意求出循环次数,若死循环输出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 0x3f3f3f3fusing 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 0x3f3f3f3fconst 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;}


1 0
原创粉丝点击