【模版】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;
}


浙公网安备 33010602011771号