【学习笔记】大步小步算法(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}} \]

posted @ 2025-04-22 20:20  tyh_27  阅读(20)  评论(0)    收藏  举报  来源