【模版】BSGS


本篇为Baby-Step-Giant-Step(BSGS)数论学习笔记。

适用场景

BSGS用于求解满足 \(b^l\equiv n\pmod p\) 的最小正整数 \(l\)

引理(毫无意义)

\(\text{Thr 1}\)

记整数集 \(S=\{b^0,b^1,\ldots,b^p\}\pmod p\)中,至少存在两个值相等。记为 \(b^i\equiv b^j\pmod p,0<i<j\),则 \(b^0\equiv b^{j-i}\)

\(\text{Thr 2}\)

思路

\(S\)\(t=\sqrt p\) 为一段的长度,分为 \(\lceil\frac{n(S)}{\sqrt p}\rceil\) 个分块,记第 \(i\) 个分块为 \(\{b^{i\cdot t},b^{i\cdot t+1},\ldots,b^{i\cdot t+t-1}\}\)
假设这样的 \(l\) 存在,则必定存在 \(t,i\),使 \(t\cdot i\leq l<t(i+1)-1\)
重新考虑

\[b^l\equiv n\pmod p \]

\(j=(i+1)t-l\), 若我们将两边同时乘以 \(b^j\),则有

\[b^{(i+1)t}\equiv n\cdot b^j\pmod p \]

我们发现,\(j\in[0,t)\),因此可以用map打出所有的 \(n\cdot b^j\)
我们还发现,\(R_{b^{(i+1)t}}=\{b^t,b^{t+1}\ldots,b^kt\}\),故\(n(R_{b^{(i+1)t}})=\lceil\frac{n(S)}{\sqrt p}\rceil\),也可以打表得。
枚举找到这样的 \((i,j)\) 后,可求出 \(l=(i+1)t-j\)

代码

在经历了5次被#11Hack的红色100分后:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7; // 998244353
const int INF = 2e9;
//const ll INF = 4e18;
const int N = (1 << 16);
ll p, b, n;
ll t, cnt;
ll qpow(ll a, ll b) {
	ll ret = 1;
	while (b) {
		if (b & 1) {
			(ret *= a) %= p;
		}
		a = a * a % p;
		b >>= 1;
	}
	return ret;
}
// 预处理n*b^(0~t)和b^kt 
ll bj[N], bk[N];
map<ll, vector<ll>> jb;
void init() {
	bj[0] = n % p;
	// 这行别漏了!!!!↓
	jb[bj[0]].push_back(0);
	for (int i = 1; i < N; ++i) {
		bj[i] = bj[i - 1] * b % p;
		jb[bj[i]].push_back(i);
	}
	bk[0] = 1;
	ll tmp = qpow(b, t);
	for (int i = 1; i <= cnt; ++i) {
		bk[i] = bk[i - 1] * tmp % p;
	}
}
int main()
{
	// freopen("BSGS.in", "r", stdin);
	//freopen(".out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> p >> b >> n;
//	printf("b^l=n mod p\n%lld^l=%lld mod %lld\n", b, n, p);
	t = sqrt(p); // 一个区块的大小 
//	printf("size of a block is %lld.\n", t);
	cnt = (p + t - 1) / t; // 区块个数 
//	printf("cnt of blocks is %lld.\n", cnt);
	init();
	if (n == 0) {
		cout << "1\n";
		return 0;
	}
	for (int i = 0; i <= cnt; ++i) {
//		printf("b(%lld)^t(%lld)*%d=%lld\n", b, t, i, bk[i]);
		auto it = jb.find(bk[i]);
		if (it == jb.end()) continue;
		for (auto j: it->second) {
			ll l = t * i - j;
//			printf("l=t*i-j\nl=%lld*%lld-%lld=%lld\n", t, i, j, l);
			if (l >= 0) { // 这一行需要考虑l可能为0!!! 
				cout << l << '\n';
				return 0;
			}
			else break;
//			printf("b(%lld)^%lld=%lld\n", b, j, it->first);
		}
//		ll l = it->second.back();
//		l = t * i - l;
//		cout << l << '\n';
//		return 0;
	}
	puts("no solution");
	return 0;
}

image

posted @ 2026-04-04 21:59  peter_code  阅读(2)  评论(0)    收藏  举报