BSGS \text{BSGS} BSGS 算法,全名 Baby Steps Giant Steps \text{Baby Steps Giant Steps} Baby Steps Giant Steps ,用于解决高次同余问题。
问题
给出 a , b , p a,b,p a,b,p ,请求出最小的非负整数 x x x ,满足 a x ≡ b ( m o d p ) a^x\equiv b\pmod p ax≡b(modp) 。其中 p p p 为质数。
解法
BSGS \text{BSGS} BSGS 板子题。
方法如下:
令
x
=
i
m
−
j
,
m
=
⌈
p
⌉
x=im-j,m=\lceil\sqrt{p}\rceil
x=im−j,m=⌈p⌉ ,则
a
i
m
−
j
≡
b
(
m
o
d
p
)
a^{im-j}\equiv b\pmod p
aim−j≡b(modp) 。
移项可得
(
a
m
)
i
≡
b
×
a
j
(
m
o
d
p
)
(a^m)^i\equiv b\times a^j \pmod p
(am)i≡b×aj(modp)
首先,从
0
−
m
0-m
0−m 枚举
j
j
j ,将得到的
b
×
a
j
b\times a^j
b×aj 的值存入
hash
\text{hash}
hash 表;
然后,从
1
−
m
1-m
1−m 枚举
i
i
i ,计算
(
a
m
)
i
(a^m)^i
(am)i ,查表,如果有值与它相等,那么这时找到的
i
m
−
j
im-j
im−j 一定是最小值。
备注
-
为什么令 m = ⌈ p ⌉ m=\lceil\sqrt p \rceil m=⌈p⌉ ?
∵ a φ ( p ) ≡ 1 ( m o d p ) ∵a^{φ(p)}\equiv 1\pmod p ∵aφ(p)≡1(modp)
∴ 方 程 的 最 小 非 负 整 数 解 x ≤ φ ( p ) ∴方程的最小非负整数解x\leq φ(p) ∴方程的最小非负整数解x≤φ(p)
∵ φ ( p ) < p ∵φ(p)<p ∵φ(p)<p
∴ x < p ∴x<p ∴x<p
所以枚举 x x x 时上界设定为 p p p 即可
按照 BSGS \text{BSGS} BSGS 算法的思想,显然此算法在 m = ⌈ p ⌉ m=\lceil \sqrt p \rceil m=⌈p⌉ 时效率最高。
但是欧拉定理的使用是有前提限制的: a , p a,p a,p 要互质。如果不满足这个条件,上面的证明也就是无鸡稽之谈。
也就是说, BSGS \text{BSGS} BSGS 算法只能在 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1 时使用。 -
i , j i,j i,j 的枚举方式?
首先我们要明确一点:枚举 j j j 时 b × a j b\times a^j b×aj 的值可能重复,这时我们要用当前较大的值取代之前的较小值。
原因很简单: j j j 越大, i m − j im-j im−j 越小嘛qwq
在我们枚举到最小的满足题意的 i i i 时,易知这时的 i m − j im-j im−j 一定是答案。
为了保证 x = i m − j > 0 x=im-j>0 x=im−j>0 ,显然 i i i 应该从 1 1 1 开始枚举。 -
时间复杂度
看起来应该是 O ( p ) \mathcal O(\sqrt p) O(p) 的?
不过如果你使用map
作为哈希表,时间复杂度会乘上一个 log \log log ,因为map
是红黑树实现的
实现
UPD on 2019/11/14 感觉还是要放个题233 \text{UPD on 2019/11/14 感觉还是要放个题233} UPD on 2019/11/14 感觉还是要放个题233
方便起见这里直接给出一道模板题
P
3846
[
T
J
O
I
2007
]
可
爱
的
质
数
\rm P3846\ [TJOI2007]可爱的质数
P3846 [TJOI2007]可爱的质数
题意很明确,就是道板子题,下面就直接放代码了
#include<cstdio>
#include<cmath>
#define rep(i,l) for (int i=l; i<=m; ++i)
const int P=10003; int cnt,p,a,b,m,s,t,res=1,last[P];
struct node { int x,i,pre; }E[1<<16];
void add(int i,int x) { E[++cnt]=(node){i,x,last[i%P]}; last[i%P]=cnt; }
int find(int i) { //查哈希表
for (int j=last[i%P]; j; j=E[j].pre) if (E[j].x==i) return E[j].i;
return -1; //-1表示没有找到
}
int main() {
scanf("%d%d%d",&p,&a,&b); m=ceil(sqrt(p)); //ceil向上取整
s=b; rep(j,0) add(s,j),s=1ll*s*a%p; s=1; rep(j,1) s=1ll*s*a%p;
rep(i,1) if (~(t=find(res=1ll*res*s%p))) return printf("%d\n",i*m-t),0;
puts("no solution"); return 0;
}
毒瘤码风不要在意qwq
这里的哈希表是哈希+拉链法直接搞的,比map
应该要快一些