【離散對數 && EXBSGS】Gym
阿新 • • 發佈:2018-12-14
Step1 Problem:
a^x ≡ b(mod m).
給你 a, b, m, 求 x.
資料範圍:
1<=T<=500, 0 <= a, b < m <= 1e9.
Step2 Ideas:
學習演算法部落格
說說自己的理解:
a^x ≡ b(mod m).
a 和 m 互質:
根據尤拉定理:如果 gcd(a, m) == 1, a^phi(m) ≡ 1(mod m).
所以 a 和 m 互質,a^x mod m 隔 phi(m) 次一定存在週期。
a^x ≡ b(mod m).
x = b * s + r:需要用逆元
由於 phi(m) < m, 所以我們令 s = sqrt(m), 列舉 x = [0, s) 求出 a^x 用 map 存起來。
當 xi = [i * s+1, (i+1) * s) 時:xi = i * s + x, a^xi ≡ b(mod m) -> a^x ≡ b * a^(-i * s)(mod m).
此時:用 map 判斷 b * a^(-i * s) 是否存在,如果存在則有解。
直到 xb = [b * s+1, (b+1) * s) 為止。
x = b * s - r:不需要用逆元
由於程式碼採用是不用逆元的方法,所以直接看程式碼即可。
a 和 m 不互質:
我們需要把方程轉換成,a 和 m 互質。
a^x ≡ b mod m -> a^x + y*m = b.
由裴蜀定理,g = gcd(a, m) 不整除 b 那麼無解返回 -1.
否則:a/g * a^(x-1) + m/g * y = b/g.
模方程:a/g * a^(x-1) ≡ b/g (mod m/g).
令 m1 = m/g, b1 = b/g * (a/g)^(-1),得到新方程:
a^x1 ≡ b1(mod m1).
可知:x = x1+1.
由於 a 是不變的,不斷重複上述操作,直到 a 和 m1 互質。
如果 b1 = 1, 此時存在解 x1 = 0. 在求出 x 即可。
Step3 Code:
#include<bits/stdc++.h> using namespace std; #define ll long long ll log_ab(ll a, ll b, ll MOD) { a = a%MOD; b = b%MOD; if(b == 1) return 0; int cnt = 0; ll t = 1; for(ll g = __gcd(a, MOD); g != 1; g = __gcd(a, MOD)) { if(b%g) return -1;//由裴蜀定理,可知無解 MOD /= g, b /= g; t = t * a / g % MOD; cnt++;//記錄 x -> x1 經過了幾次。 if(t == b) return cnt;//b1 = 1. } unordered_map<ll, ll> mp; int m = ceil(sqrt(1.0 * MOD)); ll e = 1; for(int i = 0; i < m; i++) {//將 a^i 存起來,i = [0, sqrt(m)),如果不用逆元需要將 b*a^i 存起來。 mp[e*b%MOD] = i; e = e * a % MOD; } // e = a^(sqrt(m)) ll nw = t; for(int i = 1; i <= m + 1; i++) { nw = e * nw % MOD; if(mp.count(nw)) { return i * m - mp[nw] + cnt; } } return -1; } int main() { int T, a, b, m; scanf("%d", &T); while(T--) { scanf("%d %d %d", &a, &b, &m); ll x = log_ab(a, b, m); if(x == -1) ; else printf("%lld\n", x); } return 0; }