题意
给定 $a,b$ 和模数 $p$,求整数 $x$ 满足 $a^x equiv b(mod p)$,不保证 $a,p$ 互质。
(好像是权限题,可见洛谷P4195
分析
之前讲过,可以通过设置 $x = km - r$ 而非 $x = km + r$ 避免求逆元,但是需要逆元存在,$a, p$ 互质的条件保证了这一点。
如果 $a, p$ 不互质怎么办呢?
我们想办法让他们变得互质。
具体地,设 $d_1 = gcd(a, p)$,如果 $d_1 mid b$,则原方程无解。否则我们把方程同时除以 $d_1$,得到
$$frac{a}{d_1}cdot a^{x-1} equiv frac{b}{d_1} mod (frac{p}{d_1})$$
如果 $a$ 和 $frac{p}{d_1}$ 仍不互质就再除,设 $d_2=gcd(a, frac{p}{d_1})$。如果 $d2 mid frac{b}{d_1}$,则方程无解;否则同时除以 $d_2$ 得到
$$frac{a^2}{d_1d_2}cdot a^{x-2} equiv frac{b}{d_1d_2} mod(frac{p}{d_1d_2})$$
这样不停地判断下去,直到 $a perp frac{p}{d_1d_2...d_k}$。
记 $D = prod_{i=1}^kd_i$,于是方程就变成了这样:
$$frac{a^k}{D}cdot a^{x-k} equiv frac{b}{D} mod(frac{p}{D})$$
由于 $a perp frac{p}{D}$,于是推出 $frac{a^k}{D} perp frac{p}{D}$。这样 $frac{a^k}{D}$ 就有逆元了,于是把它丢到方程的右边,就是一个普通的BSGS问题了,于是求解 $x-k$ 再加上 $k$ 就是原方程的解。
$frac{a^k}{D}$ 可能很大,事实上可以随手模 $frac{p}{D}$(显然)。
注意,不排除解小于等于 $k$,所以在消因子之前做 $O(k)$ 枚举,直接验证 $a^i equiv b mod(p)$,就能避免这种情况。
这个复杂度已经有点玄学了,普通的BSGS的复杂度为 $O(sqrt p logp)$。洛谷上100组,$a, b, p leq 1e9$,map不开O2优化会超时,需要开O2优化或者使用unordered_map。
代码
#include <cstdio> #include <cmath> #include <map> using namespace std; typedef long long ll; ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; } ll qpow(ll a, ll b, ll p) { a = a % p; ll ret = 1; while(b) { if(b&1) ret = ret * a % p; a = a * a %p; b >>= 1; } return ret % p; } ll extend_bsgs(ll a, ll b, ll p) //a^x=b(mod p),a,p不一定互质,不存在返回-1 { ll _a = a, _b = b, _p = p; a %= p; b %= p; if (a == 0) return b > 1 ? -1 : b == 0 && p > 1; ll g, cnt = 0, q = 1; while ((g = gcd(a, p)) != 1) { if (b == q) return cnt; if (b % g) return -1; ++cnt; b /= g; p /= g; q = a/g*q%p; //可以随手取模 } ll tmp = 1; for(int i = 0;i <= cnt;i++) //枚举小于等于cnt的(好像也不是必须的 { if(tmp % _p == _b) return i; tmp = tmp * _a % _p; } map<ll, ll> x; ll m = sqrt(p); for (ll i = 1, t = b*a%p; i <= m; ++i, t = t*a%p) x[t] = i; for (ll i = m, t = qpow(a, m, p); i-m < p-1; i += m) if (q = q*t%p, x.count(q)) return i-x[q]+cnt; return -1; } int main() { ll a, p, b; while (scanf("%lld %lld %lld", &a, &p, &b), p) { ll ans = extend_bsgs(a, b, p); if (ans == -1) puts("No Solution"); else printf("%lld ", ans); } return 0; }
参考链接: