Baby Step, Gaint Step
巨人的一大步是宝宝的一小步开始的。
BSGS 北上广深-介绍
求这样一个方程 \(a^x \equiv b \pmod p\),其中 \(p\) 是质数。
暴力做法是枚举 \(x\)。
考虑这样一个做法,取一个数 \(m\),先暴力求出前 \(m\) 个 \(a^1,a^2,\dots,a^m\)。然后令 \(x = mi-j(i\ge 1, 1 \le j \le m)\)。可以做出如下变换:
这里,枚举 \(i\),时间复杂度为 \(\mathcal O\left(\dfrac{p}{m}\right)\),左边均可以直接计算。
右边直接预处理前 \(m\) 个幂,用 map 判断是否存在解。显然若存在多个 \(k\in [1,m]\) 满足 \(a^k\equiv q \pmod p\),应当记录最大的 \(k\)。
时间复杂度 \(\mathcal O\left(m + \dfrac{p}{m}\log p\right)\),显然 \(m\) 取 \(\sqrt{p\log p}\) 时复杂度最优,为 \(\mathcal O(\sqrt {p \log p})\),这个复杂度只停留在理论。其实直接取 \(\sqrt p\) 也可行。
例题 1 计算器
给出 \(y,z,p\)。
- \(1\) 类询问:\(y^z \bmod p\)
- \(2\) 类询问:\(xy\equiv z \pmod p\) 的最小非负整数解。
- \(3\) 类询问:\(y^x\equiv z \pmod p\) 的最小非负整数解。
只考虑 3 类询问,是个模板。
注意:\(y,z\) 同为 \(0\) 时,答案为 \(1\);\(y,z\) 只有一个为 \(0\) 时,无解。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define LL long long
#define ULL unsigned long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define FASTIO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ojs666 false
const int N = 1e5+5,mod = 998244353;
const LL inf = 1e18;
int typ;
LL y, z, p;
inline LL qpow(LL a, int b, int p) {
LL res = 1;
for (; b; b >>= 1, a = a*a%p) if (b & 1) res = res*a%p;
return res;
}
inline void solve1() {
cin >> y >> z >> p;
cout << qpow(y, z, p) << '\n';
}
inline void solve2() {
cin >> y >> z >> p;LL inv = qpow(y, p-2, p);
if (inv == 0) cout << "Orz, I cannot find x!\n";
else cout << z*inv%p << '\n';
}
inline void bsgs(int y, int z, int p) { // x^y=z(mod p), calculate y
map<int,int> mp;
int sq = __builtin_sqrt(p);
LL tmp = 1;
for (int i = 1; i <= sq; ++i) mp[tmp=tmp*y%p] = i;
LL mul = qpow(y, sq, p), inv = qpow(z, p-2, p);
LL now = mul;
for (int j = sq; j-sq <= p; j += sq, now = now*mul%p) {
if (mp.find(now*inv%p) != mp.end()) return cout << j-mp[now*inv%p] << '\n', void();
}
cout << "Orz, I cannot find x!\n";
}
inline void solve3() {
cin >> y >> z >> p; y %= p, z %= p;
if (z == 0 && y == 0) cout << "1\n";
else if (z == 0 && y != 0 || z != 0 && y == 0) cout << "Orz, I cannot find x!\n";
else bsgs(y, z, p);
}
signed main() {
FASTIO;
int _;
cin >> _ >> typ;
if (typ == 1) { while (_--) solve1(); }
else if (typ == 2) { while (_--) solve2(); }
else if (typ == 3) { while (_--) solve3(); }
return 0;
}
例题 2 Lunar New Year and a Recursive Sequence
有一串 \(n\) 个数的数列,给你 \(b_1\sim b_k\)。当 \(i>k\) 时:
\[f_i=(\prod\limits_{j=1}^kf_{i-j}^{b_j})\bmod{998244353} \]已知 \(f_1=f_2=\cdots=f_{k-1}=1,f_n=m\),问 \(f_k\) 可能是多少。
\(k \leq 100\),\(n \leq 10^9\)。
极为套路。考虑令 \(f_k=x\),令 \(f_i=x^{c_i}\)。
显然有 \(c_1=c_2=\cdots=c_{k-1}=0,c_k=1\)。
这个就很好做了,直接矩阵快速幂得到 \(c_n\),我们得到一个 \(c_n\) 次剩余方程:
这个直接假设 \(x=g^a \bmod p\),\(g\) 是 \(p\) 的一个原根。因为 \(p=998244353\),可以钦定 \(g=3\)。
方程化为
这是一个关于 \(a\) 的高次同余方程,除了 \(a\) 所有值都已确定,直接 BSGS 做就行了。

浙公网安备 33010602011771号