【POJ】2417Discrete Logging-BSGS算法

传送门:poj2417


题解

根据欧拉定理,在模数为p的情况下,正整数b的最小循环节最多为p。
对于p以内,我们当然不能暴力枚举。
那么设 m=p m = p 上取整,我们预处理出 nb1 n ∗ b 1 nbm n ∗ b m 的值存在hash表里,存的时候若有相同的,就让后面的覆盖前面的就好了(因为我们枚举i*m-j,自然j越大越好,以保证求的的是最小值),然后枚举 im i ∗ m (1im) ( 1 ≤ i ≤ m ) 然后hash表里找对应的值就好了。
最后有可能是个负数,需要取个模。


代码

map版:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
ll mod,b,n,now,bri,ans;
int r;
map<ll,int>mp;
inline ll fp(ll a,int b)
{
    ll ret=1;
    for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod;
    return ret;
}

int main(){
    while(scanf("%lld%lld%lld",&mod,&b,&n)==3){
       b%=mod;
       if(b==0){if(n==1) printf("0\n");else printf("no solution\n");continue;}
       mp.clear();
       r=(int)(sqrt((double)mod))+1;
       now=n%mod;mp[now]=0;
       for(int i=1;i<=r;i++)  mp[now=(now*b%mod)]=i;
       bri=fp(b,r);now=1;int ptr=1;
       for(int i=1;i<=r;i++){
         now=now*bri%mod;
         if(mp[now]){
            ptr=0;
            ans=r*i-mp[now];
            printf("%lld\n",(ans%mod+mod)%mod);
            break;
         }
       }
       if(ptr==1) printf("no solution\n");
    }
    return 0;
} 

hash版:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=2e6+10;
const int hs=63713;
typedef long long ll;
ll mod,b,n,now;int lim;
int head[hs+123],to[N],nxt[N],tot,val[N];

inline void insert(int h,int id){
        for(int i=head[h%hs];i;i=nxt[i]){
            if(to[i]==h){val[i]=id;return;}
        }
        to[++tot]=h;nxt[tot]=head[h%hs];head[h%hs]=tot;
        val[tot]=id;
}   

inline int get(int h){
        for(int i=head[h%hs];i;i=nxt[i]){
            if(to[i]==h) return val[i];
        }return 0;
}
inline void itia(){tot=0;memset(head,0,sizeof(head));}
inline ll fp(ll a,int b)
{ll ret=1;for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod;return ret;}

int main(){
        while(scanf("%lld%lld%lld",&mod,&b,&n)==3){
            b%=mod;
            if(b==0){if(n==1) printf("0\n");else printf("no solution\n");continue;}
            itia();
            lim=(int)sqrt((double)mod)+1;
            ll now=n%mod;
            for(int i=1;i<=lim;i++){now=now*b%mod;insert(now,i);}
            ll bri=fp(b,lim);now=1;int ptr=1;
            for(int u,i=1;i<=lim;i++){
                now=now*bri%mod;
                if(u=get(now)){ptr=0;printf("%lld\n",((i*lim-u)%mod+mod)%mod);break;
                }else if(now%mod==n){ptr=0;printf("%lld\n",((i*lim)%mod+mod)%mod);break;}
            }   
            if(ptr) printf("no solution\n");
        }
        return 0;
}

ps:
顺便附一个拓展欧几里得:

#include<bits/stdc++.h>
using namespace std;

int a,b,x,y;

inline void excg(int a,int b)
{
    if(b){
        excg(b,a%b);
        int k=x;x=y;y=k-a/b*y;
    }else{x=1,y=0;}
}

int main(){
    scanf("%d%d",&a,&b);
    excg(a,b);
    printf("%d\n",a*x+b*y);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值