BSGS:
参考视频:https://b23.tv/U1JcUA
给你a,b,p,且p为质数,求x的最小非负整数解。
1.首先我们会想到使用暴力的方式进行求解。那就是枚举x,从0开始。
那么问题来了。是否会有穷尽呢?还是无尽的枚举下去呢?
由于p是质数,由费马小定理可以知道
a ^ (p - 1)
1 (mod p)
a ^ 0
1 (mod p)
所以幂有长度不超过p的循环节。 暴力枚举是可以得到答案的,时间复杂度为O(p)的。
但是,当p很大的时候呢? 这个时候就可以用到一个时间复杂度为O(sqrt(p))的算法进行求解了。这就是BSGS算法。
首先,设x == i * m - j; 其中 m == sqrt(p)
这个公式可以看成什么呢? 可以看成 x / m 的 除数为 i , 余数为j.
但是为什么定义为-j,而不是+ j呢? 这是为了后面的操作方便。
而由上面红色这句话,可以知道,i 小于等于 sqrt(p). j 也小于等于 m.
i 小于等于sqrt(p)是因为 i 相当于 x / m 的除数, 所以 不会超过sqrt(p)
j 小于等于sqrt(p) 是因为 如果j大于等于 sqrt(p)的话,可以转化为 新 i == i + 1, 新j = j - sqrt(p);
知道这些之后有什么用呢?
a ^ (i * m - j) mod (p) == b mod p;
所以 a ^ (i * m) mod p == b * a ^ j mod p;
于是我们从0~m枚举j.
将对应的 b * a ^ j mod p存入到哈希表中。
但是如果存在两个 b * a ^ j mod p值一样时怎么办?
我们存入更大的那个j即可。 因为我们需要的是最小的x. 所以j越大越好。
枚举完了j以后。
我们在去枚举 i,
首先通过快速幂的方式求解出a ^ m 次方
然后i 从1开始枚举 ,
得到a ^ (i * m) mod p的值。看哈希表中是否存在对应的这个值。
如果存在这个值的话,那么对应的i , j 就是我们要求出来的解。
x == i * m - j.
而如果枚举了所有以后依然不存在的话,那就是无解了。
同时这里面还有许多细节。
首先,需要对a , b 进行mod p 处理
当a mod p == 0的时候, 要有解 就必须b mod p == 0,输出最小正整数1.
如果这个时候b mod p 不等于 0 则无解。
如果b等于1. 则直接输出x == 0. 不用接着往后计算了 。(当然,也可以不对这个进行处理。后面的算法实际上也可以将这个情况下x == 0 求出。)
题目链接:1.可爱的质数 2.计算器
题目:
1.可爱的质数
题目描述
给定一个质数 pp,以及一个整数 bb,一个整数 nn,现在要求你计算一个最小的非负整数 ll,满足 b^l \equiv n \pmod pbl≡n(modp)。
输入格式
仅一行,有 33 个整数,依次代表 p, b, np,b,n。
输出格式
仅一行,如果有 ll 满足该要求,输出最小的 ll,否则输出
no solution
。输入输出样例
输入 #1复制
5 2 3
输出 #1复制
3
说明/提示
数据规模与约定
- 对于所有的测试点,保证 2\le b,n < p<2^{31}2≤b,n<p<231。
2.计算器
题目描述
你被要求设计一个计算器完成以下三项任务:
- 给定 y,z,py,z,p,计算 y^z \bmod pyzmodp 的值;
- 给定 y,z,py,z,p,计算满足 xy \equiv z \pmod pxy≡z(modp) 的最小非负整数 xx;
- 给定 y,z,py,z,p,计算满足 y^x \equiv z \pmod pyx≡z(modp) 的最小非负整数 xx。
为了拿到奖品,全力以赴吧!
输入格式
输入文件包含多组数据。
第一行包含两个正整数 T,KT,K,分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下 TT 行每行包含三个正整数 y,z,py,z,p,描述一个询问。
输出格式
输出文件包括 TT 行。
对于每个询问,输出一行答案。
对于询问类型 2 和 3,如果不存在满足条件的,则输出
Orz, I cannot find x!
。输入输出样例
输入 #1复制
3 1 2 1 3 2 2 3 2 3 3
输出 #1复制
2 1 2
输入 #2复制
3 2 2 1 3 2 2 3 2 3 3
输出 #2复制
2 1 0
输入 #3复制
4 3 2 1 3 2 2 3 2 3 3 2 4 3
输出 #3复制
0 1 Orz, I cannot find x! 0
说明/提示
测试点共分为三类,各类测试点占总测试点的比例如下:
K=K= 测试点占比 11 20\%20% 22 35\%35% 33 45\%45% 所有数据均满足:1 \leq y,z,p \leq 10^91≤y,z,p≤109,pp 是质数,1 \leq T \leq 101≤T≤10。
代码实现:
1.可爱的质数
# include <iostream>
# include <unordered_map>
# include <cmath>
using namespace std;
unordered_map<long long,long long> book;
long long qmul(long long a , long long b , long long mod)
{
long long res = 0;
while(b)
{
if(b & 1)
{
res = (res + a) % mod;
}
b >>= 1;
a = (a + a) % mod;
}
return res;
}
long long qmi(long long a,long long b,long long mod)
{
long long res = 1;
while(b)
{
if(b & 1)
{
res = qmul(res,a,mod);
}
b >>= 1;
a = qmul(a,a,mod);
}
return res;
}
/*
long long qmi(long long a , long long b , long long mod)
{
int res = 1;
while(b)
{
if(b & 1)
{
res = res * a % mod;
}
b >>= 1;
a = a * a % mod;
}
return res;
}
*/
int main()
{
long long p , b ,n;
scanf("%lld %lld %lld",&p,&b,&n);
// 求b ^ x == n (mod p);
b = b % p; // 有可能存在b % p为0的情况,这个时候n % p为0才有解。
n = n % p;
if(b == 0)
{
if(n == 0)
{
printf("1\n");
return 0;
}
printf("no solution\n");
}
if(n == 1) // 特判n == 1 的时候, 那么x就是0
{
printf("0\n");
return 0;
}
long long m = ceil( sqrt(p) ); // 取sqrt(p)并向上取整
// j 从0 开始。枚举b * a ^ j (mod p)
long long temp = n;
book[temp] = 1; // j 为0 的时候结果为p;
for(long long i = 1 ; i <= m ; i++)
{
temp = temp * b % p;
book[temp] = i + 1;
}
// 枚举i从1开始
//首先使用快速幂求解出a ^ m
temp = qmi(b,m,p);
long long t = 1;
for(long long i = 1 ; i <= m ; i++)
{
t = t * temp % p;
if(book[t] != 0)
{
printf("%lld\n",i * m - book[t] + 1);
return 0;
}
}
printf("no solution\n");
return 0;
}
2.计算器
# include <iostream>
# include <cmath>
# include <unordered_map>
using namespace std;
long long qmi(long long a, long long b , long long mod)
{
long long res = 1;
while(b)
{
if(b & 1)
{
res = res * a % mod;
}
b >>= 1;
a = a * a % mod;
}
return res;
}
long long exgcd(long long a ,long long b ,long long &x,long long &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
else
{
long long t = exgcd(b,a % b,y,x);
y -= a / b * x;
return t;
}
}
// y ^ x == z (mod p)
long long bsgs(long long y , long long z , long long p)
{
unordered_map<long long,long long> book;
book.clear();
y = y % p; // 有可能存在y % p为0的情况,这个时候z % p为0才有解。
z = z % p;
if(y == 0)
{
if(z == 0)
{
return 1; // 最小正整数为1
}
return -1;
}
long long m = ceil( sqrt(p) );
if(z == 1)
{
return 0;
}
else
{
long long temp = z;
book[z] = 1;
for(long long i = 1 ; i <= m ; i++)
{
temp = temp * y % p;
book[temp] = i + 1;
}
temp = qmi(y,m,p);
long long t = 1;
for(long long i = 1 ; i <= m ; i++)
{
t = t * temp % p;
if(book[t] != 0)
{
return i * m - book[t] + 1;
}
}
}
return -1;
}
int main()
{
long long t,k;
scanf("%lld %lld",&t,&k);
while(t--)
{
long long y,z,p;
scanf("%lld %lld %lld",&y,&z,&p);
if(k == 1) //快速幂
{
printf("%lld\n",qmi(y,z,p));
}
else if(k == 2)
{
long long x , k;
long long temp = exgcd(y,p,x,k);
if(z % temp)
{
printf("Orz, I cannot find x!\n");
}
else
{
long long t = p / temp;
x =(( (x / temp) % t) * z ) % t;
printf("%lld\n", ( x % t + t) % t);
}
}
else
{
long long t = bsgs(y,z,p);
if(t == -1)
{
printf("Orz, I cannot find x!\n");
}
else
{
printf("%lld\n",t);
}
}
}
return 0;
}