我们一般叫他北上广深算法。
Part 1. BSGS 是干什么的
求解如下问题:
已知 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 是质数。
Part 2. 做法
由欧拉定理我们可以知道:
a
φ
(
p
)
≡
1
(
m
o
d
p
)
a^{\varphi(p)}\equiv 1\pmod p
aφ(p)≡1(modp)
又因为:
a
0
≡
1
(
m
o
d
p
)
a^0\equiv 1\pmod p
a0≡1(modp)
所以可以发现 [ 0 , φ ( p ) − 1 ] [0,\varphi(p)-1] [0,φ(p)−1] 为 a i a_i ai 的循环节,考虑枚举一定范围来求解。
我们设
x
=
i
k
−
j
(
0
⩽
j
<
k
)
x=ik-j(0\leqslant j<k)
x=ik−j(0⩽j<k),可以得到
a
i
k
−
j
≡
b
(
m
o
d
p
)
a^{ik-j}\equiv b\pmod p
aik−j≡b(modp),转化为:
a
i
k
≡
b
a
j
(
m
o
d
p
)
a^{ik}\equiv ba^j\pmod p
aik≡baj(modp)
于是我们考虑将所有的
b
a
j
m
o
d
p
ba^j\bmod p
bajmodp 求出来,显然有
k
k
k 个,塞入哈希表里,枚举
i
∈
[
0
,
k
]
i\in[0,k]
i∈[0,k],查找对应的
b
a
j
ba^j
baj 值,即可得到答案。
现在我们的复杂度是
O
(
max
(
k
,
φ
(
p
)
k
)
)
\mathcal{O}\left(\max\left(k,\dfrac{\varphi(p)}{k}\right)\right)
O(max(k,kφ(p))),由基本不等式取
k
=
⌈
p
⌉
k=\lceil\sqrt{p}\rceil
k=⌈p⌉ 时时间复杂度最优。
特别要注意的是,一定不能取在
⌊
p
⌋
\lfloor\sqrt{p}\rfloor
⌊p⌋,要么向上取整,要么使用向下取整
+
1
+1
+1。
如果我们使用 map
进行哈希表处理可以得到时间复杂度
O
(
p
log
p
)
\mathcal{O}(\sqrt{p}\log p)
O(plogp),用普通的哈希表或者 unordered_map
可以做到
O
(
p
)
\mathcal{O}(\sqrt p)
O(p)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
char op=getchar();
int w=0,s=1;
while(op<'0'||op>'9'){
if(op=='-') s=-1;
op=getchar();
}
while(op>='0'&&op<='9'){
w=(w<<1)+(w<<3)+op-'0';
op=getchar();
}
return w*s;
}
int mod;
int Mul(int a,int b){return (a%mod*b%mod)%mod;}
int Add(int a,int b){return (a+b)%mod;}
int Dec(int a,int b){return (a-b+mod)%mod;}
int Pow(int a,int k){
int ans=1;
while(k){
if(k&1) ans=Mul(ans,a);
a=Mul(a,a);
k>>=1;
}
return ans;
}
map<int,int> mp;
signed main(){
int p=read(),a=read(),b=read();
mod=p;
int k=sqrt(p)+1;
for(register int i=0,cnt=b;i<=k;i++,cnt=Mul(cnt,a)) mp[cnt]=i;
int now=Pow(a,k);
for(register int i=1,cnt=now;i<=k;i++,cnt=Mul(cnt,now)){
if(mp.count(cnt)!=0){
printf("%lld",i*k-mp[cnt]);
return 0;
}
}
printf("no solution");
}