POJ_3243 Clever Y babystep

来源:互联网 发布:应用最广泛的编程语言 编辑:程序博客网 时间:2024/05/16 07:35

http://poj.org/problem?id=3243

题意:

和上一题一样,还是求A^x = B( mod C )的最小x值,但是这题和上题有个不同点就是

这题的C没有限制条件,也就是说这题并没有规定C必须是质数。

思路:

     还是用 babystep_gaintstep算法求解。但是这题并不能用POJ_2417的算法,直接套该

算法,下面简要说明一下不能用的原因。首先我们有必要归纳一下用babystep算法解题

的步骤: (1) 、求M = ceil( sqrt(C) ) ;(2) for(i=0;i<M;i++)   hash( i , A^i ) ;(3) 求D = A^M

%C;(4) 、r = 1 ; for( i = 0 ; i < M ; i++ )  ex_gcd(r , C , x , y ) ; res = x * B % C  ; jj = find( res)

如果找到了这时候的jj,则答案就是i*M+jj,如果没有找到,则res = res * D % C,继续循

环查找,如果最终都没有找到,则输出无解。 在上述的步骤中,如果题目中没有告诉我们

gcd(A , C) = 1,则我们上述的方法是错误的,原因就在于第4步,求res的时候。因为如果

我们无法保证gcd(A , C) = 1 ,也就不能保证gcd(r ,C) = 1(因为D=A^M, r = D^i),所有在

用 扩展欧几里得求出r*x + C*y = gcd(r,C ) 的一个解x0之后,原方程:r*x+C*y = B的解

x = x0 * B / gcd(r,C) + i*C / gcd(r,C) ,但是我们这个时候并不能计算出gcd(r,C),因为此时

的r本来就是经过取余之后得出的,并不能直接用来求gcd,因此我们上述的普通babystep

算法就会出错了。  

      这样我们就要换一种处理的方法了,这里介绍一种AC大牛博客上的一种“消因子”的方法,

具体内容请看这里:AC大牛。经过上面的分析我们很清楚接下去的处理应该从哪方面着

手,就是应该从不能求出gcd(r , C)入手。一种思想就是既然无法求, 那我每次只要保证

gcd(r, C) = 1那样就可以想普通babystep一样求解了,既然要保证gcd(r,C) =1 ,而 

r = (A^M)^i,因此归根到底还是要求gcd( A , C )  = 1。下面就是从AC大牛博客上参考的“消因子”

法了,每次我们 都消去A,C的一个因子,然后对B,C, D进行如下的处理:B/=tmp;C/=tmp ; 

D = D* A/tmp%C ,这样经过b轮的消因子之后,gcd(A,C) = 1, 接下去我们就可以用普通

的babystep求解出方程:A^x = B'( mod C' ) 的解 res1, 原方程的解就是 res = res1 + b。

下面给出这种方法正确的简要证明;一开始我们要求的方程是:A^x = B( mod C ),也就是

求一个最小的x,使得A^x + C*y = B,通过消因子, 我们不断在方程两遍消去gcd(A,C),这

样方程就可以变成 D*A^x1 + C'*y1 = B',很简单就可以证明上式中 x = x1 + b ; y = y1 的(只要

在方程的两边分别将消去的因子乘回去等式还是保持不变的)。这样我们的问题就转化为了

求x1和y1,即D*A^x1 = B'( mod C' ),此时gcd( A , C') = 1,这样我们就可以用普通的babystep

求出上述式子的解x1,同时也就求出了x,这样本题就解决了。 

       但是上述的方法还是有一个bug的,也就是说,我们用babystep求出的x1>=0,所以上述的

方法只能求出x >= b的解,这样我们自然就会想到如果有一个解x < b怎么办,上述方法就会出

先错误了,因此我们这里还需要改进。考虑b的最大值是多少,考虑每次我们消去的因子数都

最小也就是2,这样我们就可以得到b的最大值就是log(C),这样我们只要保证每次log(C)之内的

解都特判一下, 就不会出现我们刚才的问题了, 所以我们要在进行上述处理之前进行一次for

循环 ,特判0 - log(C)直接的x是否能成为解,接下去再用上述的“消因子”算法。

     最后不得不佩服发明这种算法的人的神奇,将O(C)复杂度的判断,分两级判断将复杂度降低

到O( sqrt(C) ),所以就是为什么叫" babystep_gaintstep "了, 哈哈。


代码:

#include<stdio.h>#include<string.h>#include<math.h>#include<stdlib.h>#define CC(m ,what) memset(m , what , sizeof(m))typedef __int64 LL ;LL A, B ,C ;const int NN = 99991 ;bool hash[NN] ;int idx[NN] , val[NN] ;void insert(int id , LL vv){    LL v = vv % NN ;    while( hash[v] && val[v]!=vv){        v++ ; if(v == NN) v-=NN ;    }    if( !hash[v] ){        hash[v] = 1;        val[v] = vv ; idx[v] = id ;    }}int find(LL vv){    LL v = vv % NN ;    while( hash[v] && val[v]!=vv){        v++ ;        if(v == NN) v-=NN ;    }    if( !hash[v] )  return -1;    return idx[v] ;}void ex_gcd(LL a , LL b , LL& x , LL& y){    if(b == 0){        x = 1 ; y = 0 ;        return ;    }    ex_gcd(b , a%b , x, y) ;    LL t = x ;    x = y;    y = t - a/b*y ;}LL gcd(LL a,LL b){    while( a%b != 0){        LL c = a ;        a = b ;        b = c % b ;    }    return b ;}LL baby_step(LL A, LL B , LL C){    LL ans = 1 ;    for(LL i=0;i<=50;i++){        if(ans == B)    return i ;        ans = ans * A % C ;    }    LL tmp , d = 0 ;    LL D = 1 % C ;    while( (tmp=gcd(A,C)) != 1 ){        if(B % tmp) return -1 ;        d++ ;        B/=tmp ;        C/=tmp ;        D = D*A/tmp%C ;    }    CC( hash , 0) ; CC( idx, -1) ; CC(val , -1) ;    LL M = ceil( sqrt(C*1.0) ) ;    LL rr = 1 ;    for(int i=0;i<M;i++){        insert(i, rr) ;        rr = rr * A % C ;    }    LL x, y ;    for(int i=0;i<M;i++){        ex_gcd(D, C , x, y) ;        LL r = x * B % C;        r = (r % C + C) % C ;        int jj = find( r ) ;        if(jj != -1){            return  LL(i)*M + LL(jj) + d ;        }        D = D * rr % C ;    }    return -1 ;}int main(){    while(scanf("%I64d %I64d %I64d",&A,&C,&B) == 3){        if(A+B+C == 0 ) break ;        LL res = baby_step(A,B,C) ;        if( res == -1 ){            printf("No Solution\n");            continue ;        }        printf("%I64d\n",res);    }    return 0 ;}

原创粉丝点击