POJ_3243 Clever Y babystep

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 ;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值