BSGS&exBSGS
一、BSGS
1. 简介
即baby(boy) step giant(girl) step算法,用于处理 \(a^x\equiv b(mod\ p)\),给 \(a,b,p\)(\(a,p\) 互质) 求所有的 \(x\) 的问题中
本质上有一点点像二分搜索的意味在里面
2. 算法流程
有 \(a^x \equiv b\mod p\)
令 \(x = A \lceil \sqrt p \rceil - B\),其中 \(0\leq A,B \leq \lceil\sqrt p \rceil\)
\(\Rightarrow a^{A\lceil \sqrt p \rceil - B} \equiv b\mod p\)
\(\Rightarrow a^{A\lceil \sqrt p \rceil} \equiv b \times a^B\mod p\)
我们可以把所有的 \(b\times a^i,i\in [1,\lceil \sqrt p\rceil]\) 存在一个映射数据结构(hash
/map
/unordered_map
)上
枚举 \(A\) (\(1\rightarrow n\)),在映射数据结构上查找是否含有 \(a^{A\lceil\sqrt p \rceil}\),
3. 时间复杂度
显然地,复杂度为 \(O(\sqrt p)\),(因为 \(A,B\leq \sqrt p\))
如果使用 map
/unordered_map
则为 \(O(\log_p \sqrt p)\)
注:建议做 UVA10225,洛谷 P3846 数据有亿点水
4. 警戒点
- \(\sqrt p\) 一定要上取整
- \(b\times a^i\),i 从0->n!
- \(a^i\),i 从1->n!
5. 代码
typedef long long ll;
map<ll,ll> Map;
ll BSGS(ll a,ll b,ll p){
Map.clear();
ll t = ceil(sqrt(p));//必须加上ceil
ll k = 1;
Map[b%p] = 0;
for(ll i = 1; i <= t; ++i)Map[(k = k * a % p) * b % p] = i;//计算b*a^i
a = 1;
for(ll i = 1; i <= t; ++i){
a = a * k % p;
if(Map.count(a))//在映射数组中找a^i
return i*t-Map[a];
}
return -1;
}
int main(){
ll p,b,n,ans;
while(~scanf("%lld%lld%lld",&p,&b,&n))
if((ans = BSGS(b,n,p)) != -1)printf("%lld\n",ans);
else puts("no solution");
return 0;
}
二、exBSGS
1. 简介
exBSGS
\(a^x\equiv b(\bmod\ p)\),给 \(a,b,p\) \((a,p\)不互质\()\)求所有的 \(x\) 的问题中
2. 算法实现
此时 \(a\times a^{x-1} \equiv b(\bmod\ p)\),令 \(d = gcd(a,p)\)
左右同除 \(d\)
\(\therefore \frac{a}{d}\times a^{x-1} \equiv \frac{b}{d}(\bmod \frac{p}{d})\)
特别的,当 \(d\nmid b\) 则无解
就可以转化为 \(a^{x-1} \equiv b^{'}\times k^{-1} (\bmod\ p^{'})\),其中 \(p^{'} = \frac{p}{d},k = \frac{a}{d},b^{'} = \frac{b}{d}\)
继续递归,直到 \(a,p\) 互质,那么再跑一遍 BSGS
即可
3. 警戒点
(1) 为了节省时间,我们将每次的 \(k\) 累乘,最后再求逆元。但是会出现一个问题,当 \(\prod k = b\) (当前的)时,我们实际上的 \(b_{real} = \frac{b}{\prod k} = 1\),此时需要退出递归,并返回 \(0\)
hack数据:a = 18 b = 16 p = 14 ans = 2
(2) 计算累乘时应该对下一轮的 \(p\) 取模
(3) 我们要在递归前对 \(a,b\) 取模,让 \(a,b < p\)
(4) 初始时 \(b = 1\) 或 \(p = 1\),我们可以直接输出 \(0\),因为 \(x = 0\) 时满足条件
hack数据:a = 18 b = 49 p = 48 ans = 0
(5) 计算逆元的时候需要使用 exgcd
而不是 快速幂
因为 \(\prod k,p\) 可能不互质
4. 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,ll> Map;
ll BSGS(ll a,ll b,ll p){
Map.clear();
ll t = ceil(sqrt(p));
ll k = 1;
Map[b%p] = 0;
for(ll i = 1; i <= t; ++i)Map[(k = k * a % p) * b % p] = i;
a = 1;
for(ll i = 1; i <= t; ++i){
a = a * k % p;
if(Map.count(a))
return i*t-Map[a];
}
return -1;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x = 1;y = 0;return a;}
ll d = exgcd(b,a%b,x,y);
ll t = x;
x = y;
y = t - (a/b) * y;
return d;
}
ll inv(ll a,ll b){
ll x,y;
exgcd(a,b,x,y);
return (x % b + b) % b;
}
ll gcd(ll x,ll y){return y ? gcd(y,x%y) : x;}
ll x = 1;//即上面说的k
ll exBSGS(ll a,ll b,ll p){
ll d = gcd(a,p);
if(b == x)return 0;//相等判定
if(d == 1){
return BSGS(a,b*inv(x,p) % p,p);//使用exgcd
}
if(b % d != 0)return -1;
else{
x = a/d * x % (p/d);
ll res = exBSGS(a,b/d,p/d);
if(res == -1)return -1;
return res + 1;
}
}
ll a,p,b;
int main(){
while(~scanf("%lld%lld%lld",&a,&p,&b)){
x = 1;
if(a == 0 && b == 0 && p == 0)break;
a%=p;b%=p;
if(b==1||p==1){
puts("0");continue;//递归之前判定
}
ll res = exBSGS(a%p,b%p,p);
if(res == -1)puts("No Solution");
else printf("%lld\n",res);
}
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/17054359.html