【学习笔记】大步小步算法(BSGS)
BSGS
题目:P3846-洛谷
题面
给定一个质数 \(p\),以及一个整数 \(g\),一个整数 \(a\),要求计算一个最小的非负整数 \(x\),满足
\[g^x \equiv a \pmod p
\]
做法
对于这种题目,可以用大步小步算法(Baby Step,Giant Step,简称 BSGS)来解决这种题目。这个算法的核心跟它的名字相似,主要在于先小步小步的处理,然后大步大步的处理,跟分块相似。
我们可以设一个 \(x\) 为小步跨的步数,\(y\) 为大步跨的步数,以及它的的步长为 \(B\)。
则可以列出方程:
\[g^{x+B \times y} \equiv a \pmod p
\]
可以将其转化为:
\[g^{B \times y} \equiv a \times (g^x)^{-1} \pmod p
\]
我们可以用 Hashmap 储存 \(a \times g^0 ,a\times (g^1)^{-1},a\times (g^2)^{-1},\dots,a\times (g^{B-1})^{-1}\)。之后我们可以枚举 \(y\),在 Hashmap 中查找是否有 \(x\) 满足条件。
复杂度为 \(\mathcal{O}(\frac{\varphi(p)}{B}+B\log p)=\mathcal{O}(\frac{p}{B}+B\log p)\)。
我们可以先求出 \((g^1)^{-1}\),每次将 \((g^x)^{-1}\) 乘 \((g^1)^{-1}\),则可以在 \(\mathcal{O}(\frac{p}{B}+B)\) 求出解。
当 \(B=\sqrt p\),复杂度最小,为 \(\mathcal{O}(\sqrt p)\)。
Code
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define mod 998244353
#define ll long long
#define db double
#define pb push_back
#define MS(x,y) memset(x,y,sizeof x)
using namespace std;
const int N=1e5+5,M=1e5+5;
const ll INF=1ll<<60;
unordered_map<ll,ll> m;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll calc(ll a,ll p){//求解逆元
ll x=0,y=0;
exgcd(a,p,x,y);
x=(x%p+p)%p;
return x;
}
ll BSGS(ll g,ll a,ll p){
a%=p;
ll B=(ll)sqrt(p)+1;
ll k=a,ng=calc(g,p),gB=g;//gB为g^B,方便求y
m[k]=1;
for(int i=1;i<B;i++){
gB=gB*g%p;
k=k*ng%p;
if(!m[k]) m[k]=i+1;//这里需要判断一下,因为k%p可能有重复情况,取第一个出现的
}
ll temp=1;
for(int i=0;i<=B;i++){
if(m[temp]) return (i*B%p+m[temp]-1)%p;
temp=temp*gB%p;
}
return -1;//无解
}
void solve(){
ll p,b,n;
cin>>p>>b>>n;
ll ans=BSGS(b,n,p);
if(ans==-1) cout<<"no solution\n";
else cout<<ans<<"\n";
return ;
}
int main(){
IOS;int T=1;
// cin>>T;
while(T--) solve();
return 0;
}
\[\large{\mathbb{END}}
\]

浙公网安备 33010602011771号