【离散对数BSGS】总结

BSGS算法简述

对于一个方程:

AxB(mod C) A x ≡ B ( m o d   C )

求能满足条件的最小的 x(GCD(A,C)=1) x ( G C D ( A , C ) = 1 )
由于 GCD(A,C)=1 G C D ( A , C ) = 1 ,所以一定满足 Aφ(C)1(mod C) A φ ( C ) ≡ 1 ( m o d   C ) 则必然有解。
在此基础上,很容易发现最优解一定在 [0,C) [ 0 , C ) 中

利用分块的思想,取 m=C m = C
i=x/m,j=x%m i = x / m , j = x % m
那么原式即可表示为:
Aim+jB(mod C) A i ∗ m + j ≡ B ( m o d   C )
两侧同乘 Aim A − i ∗ m ,可得
B(Am)iAj(mod C) B ∗ ( A − m ) i ≡ A j ( m o d   C )
于是,我们就可以通过枚举 (Am)iB ( A − m ) i ∗ B ,同时查找是否其值刚好为 Aj(j[0,m)) A j ( j ∈ [ 0 , m ) )
可以将 Aj(j[0,m))O(m) A j ( j ∈ [ 0 , m ) ) 用 O ( m ) 预处理,并存入hash表中(map)
这样一来,整个算法的复杂度就降到了 O(N) O ( N )

拓展BSGS

在普通的BSGS中,存在一个极为严苛的前提 (GCD(A,C)=1) ( G C D ( A , C ) = 1 )
若不满足这个条件,是否仍然能求出最小的x呢?

这就是拓展BSGS的用处了。
现在再回来看这个式子:

AxB(mod C) A x ≡ B ( m o d   C )

首先,对于任意一个数 d d ,若d|A,d|C则必然满足 d|B d | B (除非 B=1 B = 1
这一点应该很显然,因为我们可以将取模用乘法来表示,这样由于A,C均为d的倍数,将其都移至左侧,则右侧只有B,其也必须是d的倍数才能满足。

假设现在我们从A,B,C中都除以d,原式则变为:

AdAx1Bd(mod Cd) A d ∗ A x − 1 ≡ B d ( m o d   C d )

由于 Ad A d 是一个常数,所以我们可以将其全部设为 D D
即原式为
DAx1Bd(mod Cd)

这样一来,我们相当于把原问题的C转化为了 Cd=C C d = C ′ ,B转化为了 Bd=B B d = B ′
持续以上操作,直到 GCD(A,C)=1 G C D ( A , C ′ ) = 1 ,若中途任意时刻, B B <script type="math/tex" id="MathJax-Element-28">B</script>不为d的倍数,则停止,判断为无解。
最后,只要套回原问题解决即可,只是左侧多了一个D(常数)

模板题(普通BSGS)POJ2417:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65536
#define MAXP 65537
using namespace std;
long long fsp(long long x,long long y,long long mod){
    long long res=1;
    while(y){
        if(y&1ll)
            res=(res*x)%mod;
        y>>=1ll;
        x=(x*x)%mod;
    }
    return res;
}
vector<int> num[MAXP+100],tr[MAXP+100];
void Insert(long long x,int val){
    long long x1=x%MAXP;
    for(int i=0;i<num[x1].size();i++)
        if(tr[x1][i]==x)
            return ;
    num[x1].push_back(val);
    tr[x1].push_back(x);
}
int read(long long x){
    long long x1=x%MAXP;
    for(int i=0;i<num[x1].size();i++)
        if(tr[x1][i]==x)
            return num[x1][i];
    return -1;
}
int solve(long long n,long long a,long long b){
    long long m=1ll*ceil(sqrt(n+0.5));
    long long v=fsp(fsp(a,m,n),n-2,n);
    long long e=1;
    for(int i=0;i<m;i++){
        Insert(e,i);
        e=(e*a)%n;
    }
    e=b;
    for(int i=0;i<m;i++){
        int res1=read(e);
        if(res1>=0)
            return i*m+res1;
        e=(e*v)%n;
    }
    return -1;
}
int main(){
    long long p,n,b;
    while(SF("%lld%lld%lld",&p,&b,&n)!=EOF){
        memset(num,0,sizeof num);
        memset(tr,0,sizeof tr);
        int res=solve(p,b,n);
        if(res>=0)
            PF("%d\n",res);
        else
            PF("no solution\n");
    }
}

模板题(拓展BSGS)POJ3243:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65536
#define MAXP 65537
using namespace std;
int numc;
long long fsp(long long x,long long y,long long mod){
    long long res=1;
    while(y){
        if(y&1ll)
            res=(res*x)%mod;
        y>>=1ll;
        x=(x*x)%mod;
    }
    return res;
}
vector<int> num[MAXP+100],tr[MAXP+100];
void Insert(long long x,int val){
    long long x1=x%MAXP;
    for(int i=0;i<num[x1].size();i++)
        if(tr[x1][i]==x)
            return ;
    num[x1].push_back(val);
    tr[x1].push_back(x);
}
int read(long long x){
    long long x1=x%MAXP;
    for(int i=0;i<num[x1].size();i++)
        if(tr[x1][i]==x)
            return num[x1][i];
    return -1;
}
long long exgcd(long long a,long long b,long long &x,long long &y){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    long long g=exgcd(b,a%b,x,y);
    long long xx=x,yy=y;
    y=xx-a/b*yy;
    x=yy;
    return g;
}
int solve(long long a,long long b,long long n,long long d){
    long long m=1ll*ceil(sqrt(n+0.5));
    long long e=d%n;
    for(int i=0;i<=m;i++){
        Insert(e,i);
        e=(e*a)%n;
    }
    long long xx,yy;
    long long v=fsp(a,m,n);
    long long g=exgcd(v,n,xx,yy);
    v=xx/g;
    v=(v%(n/g)+(n/g))%(n/g);
    if(v==0)
        v=(n/g);
    e=b;
    for(int i=0;i<m;i++){
        int res1=read(e);
        if(res1>=0)
            return i*m+res1;
        e=(e*v)%n;
    }
    return -1;
}
long long prepare(long long a,long long &b,long long &n){
    long long res=1,xx,yy,a1=1%n;
    long long g=exgcd(a,n,xx,yy);
    for(int i=0;i<=100;i++){
        if(a1==b){
            PF("%d\n",i);
            return 0;
        }
        a1=(a1*a)%n;
    }
    while(g!=1){
        n=n/g;
        res=(res*(a/g))%n;
        numc++;
        if(b%g)
            return -1;
        b/=g;
        g=exgcd(a,n,xx,yy);
    }
    return res;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    long long a,n,b;
    while(SF("%lld%lld%lld",&a,&n,&b)!=EOF){
        if(!a&&!b&&!n)
            break;
        a%=n;
        b%=n;
        memset(num,0,sizeof num);
        memset(tr,0,sizeof tr);
        numc=0;
        long long d=prepare(a,b,n);
        if(d==0)
            continue;
        if(d<0){
            PF("No Solution\n");
            continue;
        }
        int ans=solve(a,b,n,d);
        if(ans>=0)
            PF("%d\n",ans+numc);
        else
            PF("No Solution\n");
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值