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\)

然后就没了呗。

P2485 BSGS模板题

#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;
}
posted @ 2025-07-16 22:08  風月華  阅读(31)  评论(0)    收藏  举报