目录
一.什么是大步小步算法(BSGS)
已知a,b,p,求x。
形如这样的同余式问题,可以用大步小步算法解决:直接将所有存入map,再用来对拍,找到合法的m和k,最后。
证明:
设,那么原式可变为
又可变为
二.模板
出自一道模板题:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
using namespace std;
#define LL long long
map <LL, LL> a;
LL B, P, N, ans;
inline LL ceil (LL x){
if (sqrt (x) * sqrt (x) != x)
return sqrt (x) + 1;
return sqrt (x);
}
int main (){
while (~scanf ("%lld %lld %lld", &P, &B, &N)){
LL len = ceil (P), t = N, m = 1, n = 1;
ans = 0;
a.clear ();
for (register int i = 1; i <= len; i ++){
m = m * B % P;
t = t * B % P;
a[t] = i;
}
for (register int i = 1; i <= len; i ++){
n = m * n % P;
if (! a[n])
continue;
ans = 1;
printf ("%I64d\n", i * len - a[n]);
break;
}
if (! ans)
printf ("no solution\n");
}
}
三.进阶例题:计算机
1.题目
2.题解
这道题一共有三个问,我们一个一个的解答:
1.直接用快速幂算
2.
证明:(是y的逆元)
3.用BSGS的模板
就这样就完的。
但是,有一个细节需要扣。就是z和y在运算前需要模p,因为在运算前我们还要判断y等于0时,z是否等于0,是则输出1,否则输出无解。
3.代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <cmath>
using namespace std;
#define LL long long
LL T, K, y, z, p;
map <LL, LL> A;
LL qkpow (LL a, int b){
LL sum = 1;
while (b > 0){
if (b % 2 == 1)
sum = sum * a % p;
a = a * a % p;
b /= 2;
}
return sum;
}
void BSGS (){
if(y == 0 && z == 0){printf("1\n"); return ;}
if(y == 0 && z != 0){printf("Orz, I cannot find x!\n"); return ;}
LL len = ceil (sqrt (double (p))), t = z, m = 1, n = 1;
A.clear ();
for (int i = 1; i <= len; i ++){
t = t * y % p;
m = m * y % p;
A[t] = i;
}
for (int i = 1; i <= len; i ++){
n = n * m % p;
if (! A[n])
continue;
printf ("%lld\n", i * len - A[n]);
return ;
}
printf ("Orz, I cannot find x!\n");
}
int main (){
scanf ("%lld %lld", &T, &K);
while (T --){
scanf ("%lld %lld %lld", &y, &z, &p);
if (K == 1)
printf ("%lld\n", qkpow (y, z));
y %= p;
z %= p;
if (K == 2){
if (y == 0 && z != 0){
printf ("Orz, I cannot find x!\n");
continue;
}
LL x = qkpow (y, p - 2) * z % p;
printf ("%lld\n", x);
}
if (K == 3){
BSGS ();
}
}
return 0;
}