【数学】扩展 BSGS
前置芝士:\(\rm BSGS\)
当 \(a,b,p\) 均为整数时,请求出方程
\[a^x\equiv b\pmod p
\]
的最小非负整数解.
对于普通的 \(\rm BSGS\),必须在 \(a,p\) 互质时才能求.
当 \(a,p\) 不互质时,需要用到扩展 \(\rm BSGS\),即 \(\rm exBSGS\).
我们考虑:
\[a^x\equiv b\pmod p\\
设 \gcd(a,p)=g_1\\
\dfrac{a^x}{g_1}\equiv \dfrac{b}{g_1}\pmod{\dfrac{p}{g_1}}
\]
那么当 \(g_1\nmid b\) 时,无解.
否则继续:
\[a^{x-1}\cdot\dfrac{a}{g_1}\equiv \dfrac{b}{g_1}\pmod{\dfrac{p}{g_1}}\\
设 \gcd(a,\dfrac{p}{g_1})=g_2\\
a^{x-2}\cdot\dfrac{a^2}{g_1g_2}\equiv\dfrac{b}{g_1g_2}\pmod{\dfrac{p}{g_1g_2}}
\]
一直往下除,直到 \(a,\dfrac{p}{g_1g_2\dots g_k}\) 互质,其中 \(k\) 是除的次数.
我们设 \(g_1g_2\cdots g_k=f\),那么最终就是
\[a^{x-k}\cdot\dfrac{a^k}{f}\equiv\dfrac{b}{f}\pmod{\dfrac{p}{f}}
\]
这时 \(a,\dfrac{p}{f}\) 互质,所以 \(a^k,\dfrac{p}{f}\) 互质,则 \(\dfrac{a^k}{f},\dfrac{p}{f}\) 互质,那么 \(\dfrac{a^k}{f}\) 在模 \(\dfrac{p}{f}\) 的意义下有逆元,所以
\[a^{x-k}\equiv\dfrac{b}{f}\cdot\operatorname{inv}(\dfrac{a^k}{f})\pmod{\dfrac{p}{f}}
\]
这样就转化成了一个普通的 \(\rm BSGS\).
预处理的时间复杂度为 \(O(p\log p)\)。
\(\text{Code}\)
#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
typedef long long ll;
using namespace std;
int phi(int p)
{
int ans = p;
for (int i = 2; (ll)i * i <= (ll)p; i++)
{
if (p % i == 0)
{
ans = ans / i * (i - 1);
while (p % i == 0)
{
p /= i;
}
}
}
if (p != 1)
{
ans = ans / p * (p - 1);
}
return ans;
}
int bsgs(int a, int b, int p)
{
map<int, int> hash;
int t = ceil(sqrt(phi(p))), aj = 1;
for (int j = 0; j < t; j++)
{
int val = (ll)b * aj % p;
hash[val] = j;
aj = (ll)aj * a % p;
}
a = aj;
if (!a)
{
if (!b)
{
return 1;
}
return -1;
}
int ai = 1;
for (int i = 0; i <= t; i++)
{
if (hash.find(ai) != hash.end())
{
int j = hash[ai];
if (i * t - j >= 0)
{
return i * t - j;
}
}
ai = (ll)ai * a % p;
}
return -1;
}
int gcd(int a, int b)
{
if (!b)
{
return a;
}
return gcd(b, a % b);
}
int x, y;
void exgcd(int a, int b)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b);
int tmp = x;
x = y;
y = tmp - a / b * y;
}
int inv(int a, int p)
{
exgcd(a, p);
x = (x % p + p) % p;
return x;
}
int exbsgs(int a, int b, int p)
{
if (b == 1 || p == 1) //特判
{
return 0;
}
a %= p, b %= p;
int g = gcd(a, p), f = 1, k = 0;
while (g > 1)
{
if (b % g)
{
return -1;
}
k++;
b /= g, p /= g;
f = (ll)f * (a / g) % p; //一定要先把 p 除以 g 再取模
if (f == b) //相等就可以直接返回
{
return k;
}
g = gcd(a, p);
}
int res = bsgs(a, (ll)b * inv(f, p) % p, p);
if (res == -1)
{
return -1;
}
return res + k;
}
int main()
{
int a, b, p;
while (scanf("%d%d%d", &a, &p, &b), a || b || p)
{
int res = exbsgs(a, b, p);
if (res == -1)
{
puts("No Solution");
}
else
{
printf("%d\n", res);
}
}
return 0;
}