P3846 [TJOI2007] 可爱的质数/【模板】BSGS

###BSGS

首先了解一下BSGS是用来干什么的,我们知道,扩展欧几里得算法可以用来解线性同余方程,那么像 atb(mod p)(gcd(a,p)=1)a^t\equiv b(mod~p)(gcd(a,p)=1) 这样的方程,就是用BSGS来解决的

根据欧拉定理 acac mod ϕ(m)gcd(a,m)=1a^c\equiv a^{c~mod~\phi(m)}gcd(a,m)=1 可以知道 tt 是每 ϕ(p)\phi(p) 次一循环,所以只需要枚举 00~phi(p)1phi(p)-1 就行了,不妨直接枚举 pp

可以把 pp 划分成长度为 k=p+1k=\sqrt{p}+1 的若干块,也可以大于这个长度,令 t=kxy,x[1,k],y[0,k1]t=kx-y,x\in[1,k],y\in[0,k-1],这样就能枚举到所有的值了

akxb×ay(mod p)\Rightarrow a^{kx}\equiv b\times a^y(mod~p)

直接通过分块处理即可,然后预处理出所有的 b×ayb\times a^y 的值对应的 yy,在枚举 xx 时直接查询是否存在对应的 yy 使得 akxb×aya^{kx}\equiv b\times a^y 即可,时间复杂度 O(n)O(\sqrt{n})

注意先特判0,因为0有可能会被其它的 yy 覆盖

结合代码

#include<iostream>
#include<cstdio>
#include<unordered_map>
#include<cmath>
using namespace std;
typedef long long ll;

int BSGS(int a, int p, int b)
{
if (1 % p == b % p)
return 0;
unordered_map<int, int> hash;
int k = sqrt(p) + 1;
for (int i = 0, j = b % p; i < k; i++, j = (ll)j * a % p)
hash[j] = i;
int ak = 1;
for (int i = 1; i <= k; i++)
ak = (ll)ak * a % p;
for (int i = 1, j = ak; i <= k; i++, j = (ll)ak * j % p)
if (hash.count(j))
return i * k - hash[j];
return -1;
}

int main()
{
int a, p, b;
scanf("%d%d%d", &p, &a, &b);
int res = BSGS(a, p, b);
if (res == -1)
printf("no solution\n");
else
printf("%d\n", res);
return 0;
}

###exBSGS

P4195 【模板】扩展BSGS

相比于朴素版的BSGS,exBSGS不一定满足 gcd(a,p)=1gcd(a,p)=1

可以分类讨论一下,同样先特判0

1.gcd(a,p)=1gcd(a,p)=1,直接调用朴素版BSGS

2.gcd(a,p)1gcd(a,p)\ne 1

d=gcd(a,p)d=gcd(a,p)

将方程转化一下 at+kp=b\Rightarrow a^t+kp=b

显然若 gcd(b,d)=1gcd(b,d)=1,则无解

反正 ad×at1bd(mod pd)at1bd×(ad)1(mod pd)\Rightarrow \frac{a}{d}\times a^{t-1}\equiv\frac{b}{d}(mod~\frac{p}{d}) \Rightarrow a^{t-1}\equiv\frac{b}{d}\times(\frac{a}{d})^{-1}(mod~\frac{p}{d})

这样我们就有构造出了一个新的方程,一直递归下去,直到 gcd(a,p)=1gcd(a,p)=1

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<unordered_map>
#define INF (1e8)
using namespace std;
typedef long long ll;

int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}

int BSGS(int a, int p, int b)
{
if (1 % p == b % p)
return 0;
unordered_map<int, int> hash;
int k = sqrt(p) + 1;
for (int i = 0, j = b % p; i < k; i++, j = (ll)j * a % p)
hash[j] = i;
int ak = 1;
for (int i = 1; i <= k; i++)
ak = (ll)ak * a % p;
for (int i = 1, j = ak % p; i <= k; i++, j = (ll)j * ak % p)
if (hash.count(j))
return i * k - hash[j];
return -INF;
}

int exBSGS(int a, int p, int b)
{
b = (b % p + p) % p;
if (1 % p == b % p)
return 0;
int x, y;
int d = exgcd(a, p, x, y);
if (d > 1)
{
if (b % d)
return -INF;
exgcd(a / d, p / d, x, y);
return exBSGS(a, p / d, (ll)b / d * x % (p / d)) + 1;
}
return BSGS(a, p, b);
}

int main()
{
int a, p, b;
while (scanf("%d%d%d", &a, &p, &b) && (a || p || b))
{
int res = exBSGS(a, p, b);
if (res == -INF)
printf("No Solution\n");
else
printf("%d\n", res);
}
return 0;
}