BSGS、exBSGS
BSGS
\(\text{BSGS}\)(\(\text{Baby Step Giant Step}\))是用来解决\(a^x \equiv b \pmod p\)的问题。
(a,p)=1
令\(m=\lceil \sqrt{p} \rceil\),则\(x\)可以表示成\(mq+r\),其中\(0 \leq q \leq m\),\(0 \leq r < m\)。
那么\(a^x\equiv b \pmod p\)可以写成\(a^{mq+r} \equiv b \pmod p\)。
移项得:\(a^{mq} \equiv ba^{-r} \pmod p\)。
现在问题变成了:找出一组\(q\),\(r\),使得上式成立,我们枚举\(q\),将左边得到的式子的所有种取值哈希起来,然后再枚举\(r\),判断右边的取值是否在左边的取值中出现,如果出现就找到了解。
枚举部分的复杂度\(\text{O}(\sqrt p)\),求逆元的复杂度为\(\text{O}(\log p)\),所以总复杂度为\(\text{O}(\sqrt p + \log p)\)。
如果你不愿意求逆也可以,只要设\(x=mq-r\),然后\(0 < q \leq m\),\(0 \leq r < m\),然后移项过程中\(-r\)变成\(+r\),于是就不用逆元了。
unordered_map<int, int> Hash;
int BSGS(int a, int b) { // a^x=b(P)
if (!a) return b ? -1 : 1; // 0^x=0的特判
Hash.clear(); // 清空
int m = (int)ceil(sqrt(P+0.5)), am = qpow(a, m), t = 1; // 求出m,am
for (int i = 1; i <= m; i++, t = 1ll*t*am%P) if (!Hash.count(t)) Hash[t] = i; // 将a^mi插入(不可重复插入,否则大的会覆盖小的)
int ans = -1; t = b;
rep0(i, m) {
unordered_map<int, int>::iterator it = Hash.find(t); // 查看是否存在
if (it != Hash.end()) {
int x = it->second * m - i;
if (ans == -1 || x < ans) ans = x;
}
t = 1ll*t*a%P;
}
return ans; // -1表示无解
}
(a,p)≠1
这种情况下,\(a\)的逆元不存在,所以我们要约分两边使得\(a\)的逆元出现。
对于\(a^x \equiv b \pmod p\),提出一个\(d=(a,p)\)并约分得到\(a^{x-1}\frac{a}{d} \equiv \frac{b}{d} \pmod {\frac{p}{d}}\)。当然要有\(d \mid b\),否则无解。
继续,如果\(d'=(a,\frac{p}{d}) \neq 1\),再拿出一个\(a\)提取因子,得到\(a^{x-2}\frac{a}{d}\frac{a}{d'} \equiv \frac{b}{dd'} \pmod {\frac{p}{dd'}}\)
以此类推,直到其互质,此时有\(a^{x-t}\frac{a^t}{\prod d} \equiv \frac{b}{\prod d} \pmod {\frac{p}{\prod d}}\)
显然此时\(\frac{a^t}{\prod d}\)存在逆元,于是可以移过去得到\(a^{x-t} \equiv \frac{b}{\prod d}\frac{a^{-t}}{\prod d} \pmod {\frac{p}{\prod d}}\),然后就成了上面的问题。
这里约分时由于约分和求公约数,多了一个\(\text{O}(\log^2 p)\)的复杂度,其他不变。
int exBSGS(int a, int b, int P) {
if (!a) return b ? -1 : 1; // 直接特判
int t = 0, d, l = 1;
while (d = gcd(a, P), d > 1) {
if (b % d) return -1; // 不能整除一定无解
P /= d, b /= d, l = 1ll*l*(a/d)%P, t++; // 约分,记录指数,记录系数
}
a %= P, b = 1ll*b*inv(l, P)%P; // 维护新的a,b
int ans = BSGS(a, b, P); // 求解
return ~ans ? ans+t : -1; // 补上t或者无解
}