BSGS算法
北上广深算法解决形式如下的问题。
\[a^x \equiv b \bmod p , p \in prime
\]
先想一下暴力的做法,即枚举 \(x\),欧拉定理可得复杂度为 \(O(p)\)。欧拉定理内容若不会的话自行百度。
不够优秀的算法。于是我们写下这样的一个等式。
\[x = mA - k
\]
这其中, \(A\) 是我们自己规定的常量。由欧拉定理可得 \(x \leq \varphi(p) = p - 1\) , 即 \(mA - k \leq p - 1\) 。
我们对原式进行变形。
\[a^{mA - k} \equiv b
\]
\[a^{mA} \equiv b \times a^k
\]
现在看一下,枚举等式的两边复杂度都是多少捏?把两边枚举一下再比对一下是否相等,就能得出答案了。
左边,由于 \(k < A\),所以 \(\lfloor \frac{p - 1}{A}\rfloor\) 和 \(\lfloor \frac{p - 1 + k}{A} \rfloor\) 值相差不超过 1。那么 \(m\) 的范围大概是 \([0 , \lfloor \frac{p}{A}\rfloor]\)。
枚举复杂度即为 \(O(\frac{p}{A})\)。
右边, \(k\) 范围是 \([0 , A)\)。枚举复杂度即为 \(O(A)\)。
总复杂度为 \(O(\frac{p}{A}) + O(A)\) , 当 \(A\) 取 \(\sqrt p\) 时复杂度最低,即 \(O(\sqrt p)\)。
这里要说明一下,代码中关于 \(m\) 的范围不能从 0 开始枚举。原因是用来对照的 map 里初始化都是 0,解决方案是加个答案是 0 的特判,然后从 1 开始枚举 \(m\)。
然后就没了呗。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int b , p , n;
map<int , int> consist;
namespace BSGS {
inline int Pow(int a , int b , int p) {
int ans = 1;
while(b) {
if(b & 1) {ans = ans * a % p;}
a = a * a % p; b >>= 1;
} return ans;
}
void bsgs(int b , int n , int p) {
n %= p;
int A = ceil(__builtin_sqrt(p));
for(register int i = 0; i < A; ++i , n = n * b % p) {
consist[n] = i;
}
int base = Pow(b , A , p);
for(register int i = 1 , present = base; i <= ((p - 1) / A); ++i , (present = present * base % p)) {
if((consist.find(present) != consist.end()) && (i * A >= consist[present])) {
cout << i * A - consist[present]; return;
}
}
cout << "no solution";
}
}
signed main() {
cin >> p >> b >> n;
if(n == 1) {
cout << 0; return 0;
}
BSGS :: bsgs(b , n , p);
return 0;
}

浙公网安备 33010602011771号