前言:
今天更BSGS算法。俗称大步小步算法(Big-Step G…-Step),又称拔山盖世、北上广深、白色狗屎 。
问题:
求解指数同余方程: a x ≡ b ( m o d p ) a^{x}\equiv b(mod\; p) ax≡b(modp)的最小自然数解。
BSGS算法:模板题
这个算法只考虑 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1的情况。
那么,怎么做呢。
其实做法很简单,就是枚举。
先来一种暴力枚举。
就是枚举 x x x从 [ 0 , p − 1 ] [0,p-1] [0,p−1],然后检验答案是否正确。
知道欧拉定理的应该知道只要枚举到 φ ( p ) − 1 \varphi(p)-1 φ(p)−1,但 p p p为质数时欧拉函数仍为 p − 1 p-1 p−1。
看过阶与原根的应该知道只要枚举到 p p p的阶即可,但算 p p p的阶仍然要暴力去枚举。
所以这里只要一个一个枚举过去便好。
但是 o ( p ) o(p) o(p)的暴力枚举配不上这么高大上的名字,所以还有优化的余地。
我们可以考虑折半枚举。
怎么折半,就是这个算法的精髓了。
我们令 x = k n + i x=kn+i x=kn+i,其中 n n n为我们设置的一个定值, k k k和i i i i均为变量。
这样原式就变为 a k n + i ≡ b ( m o d p ) a^{kn+i}\equiv b(mod\;p) akn+i≡b(modp)。
由于 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,我们可以将 a i a^{i} ai的逆元乘到右边,变成 a k n ≡ b ( a i ) − 1 ( m o d p ) a^{kn}\equiv b(a^{i})^{-1}(mod\;p) akn≡b(ai)−1(modp)。
这样我们可以枚举所有的 i i i,将其对应右边的值存入哈希表(也可用unordered_map)。然后枚举 k k k的过程中,去表中找满足的 i i i,找到后 k n + i kn+i kn+i就是答案。当然因为要找最小的,枚举过程中动点手脚就好了。
那么这个 n n n的设置嘛,利用分块的思想, n n n设为 p \sqrt{p} p就好了,这样 k k k只用枚举到 p \sqrt{p} p, i i i也只用枚举到 p \sqrt{p} p。总的复杂度为 n \sqrt{n} n。
上代码:
#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll fpow(ll a,ll n,ll mod)
{
ll sum=1,base=a%mod;
while(n!=0)
{
if(n%2)sum=sum*base%mod;
base=base*base%mod;
n/=2;
}
return sum;
}
ll ex_gcd(ll a,ll b,ll& x,ll& y)
{
if(b==0)
{
x=1;y=0;
return a;
}
ll ans=ex_gcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
return ans;
}
ll inv