简要题意:给定一个质数 \(p\),以及一个整数 \(a\),一个整数 \(n\),现在要求你计算一个最小的非负整数 \(x\),满足 \(a^x \equiv n \pmod p\).
本题是 BSGS (BaBy Step Giant Step)
,即大步小步算法。
下面我们将一步步引入该算法。考虑令 \(x = rt - s\),其中 \(s < t\),而 \(t\) 是我们给定的一个类似于 “块长” 的东西。
\[\begin{aligned}
& a^{rt - s} \equiv n \pmod p \\
& a^{rt} \equiv n \times a^s \pmod p \\
& (a^t)^r \equiv n \times a^s \pmod p \\
\end{aligned}
\]
注意到,这里我们可以用哈希进行处理。
\(n \times a^s\) 的值最多有 \(t\) 个,因为 \(s < t\).
而 \((a^t)^r\) 的值最多有 \(\lfloor \frac{p}{t} \rfloor\),因为 \(x \leq p\).
哈希自带一个 \(\text{log}\),加上快速幂,于是该程序时间复杂度为 \(\mathcal{O}(t \log^2 t + \frac{p}{t} \log^2 \frac{p}{t})\).
很显然,块长 \(t\) 在取到 \(\lfloor \sqrt{p} \rfloor\) 时时间复杂度最优。
时间复杂度:\(\mathcal{O}(\sqrt{p} \log^2 \sqrt{p})\). 这在 \(p = 10 ^ {12}\) 的时候仍然是足以通过的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll p,a,n;
map<ll,ll> m;
inline ll pw(ll x,ll y) {
ll ans=1; while(y) {
if(y&1) ans=ans*x%p;
x=x*x%p; y>>=1;
} return ans;
}
int main() {
scanf("%lld %lld %lld",&p,&a,&n);
ll t = sqrt(p);
for(ll s = 0 ; s < t ; s++) m[n * pw(a,s) % p] = s; // 计算式子右侧的值
a = pw(a,t);
ll ans = 1e17;
for(ll r = 1 ; r <= t; r++)
if(m[pw(a,r)]) ans = min (ans , r * t - m[pw(a,r)]); // 寻找对应
printf(ans == 1e17 ? "no solution" : "%d",ans);
puts("");
return 0;
}