【Coel.学习笔记】大步小步算法(Baby Step Giant Step)
题前碎语
月考结束,年级50,心情愉悦,来写代码。
(建议无视这段b话
总之,月考终于结束了,排名比段考进了一大步,非常开心
(原本还以为会考得很差来着,没想到大家比我还烂,哈哈哈)
笔记内容
本笔记含有大步小步算法。
大步小步算法
大步小步算法(英文名\(Baby\) \(Step\) \(Giant\) \(Step\),又名拔山盖世、北上广深、不是个事,以下简称\(BSGS\))是一种用来解决高次同余方程的算法,其时间复杂度为\(O(\sqrt{n})\)。
高次同余方程的基本形式为:
\[a^x\equiv b\pmod m
\]
其中保证\(a\)与\(m\)互质。
互质?这不就是欧拉定理吗?
\[a^{\varphi(m)}\equiv 1\pmod m
\]
当然,高次同余方程确实可以用欧拉定理加上暴力枚举解决,但很可惜的是,欧拉定理的时间复杂度为\(O({\varphi(m)})\)。我们知道,欧拉函数\(\varphi(m)\)表示与\(m\)互质且小于等于\(m\)的整数个数,因此时间复杂度的最大值为\(O(m)\),此时\(m\)为质数。
而\(BSGS\)可以对上述方式进行优化(以下内容引用自OI Wiki):
令
\[x=A\left\lceil\sqrt{p}\right\rceil -B
\]
则有
\[a^{A\left\lceil\sqrt{p}\right\rceil}\equiv ba^B\pmod p
\]
考虑枚举\(ba^B\)的所有取值,枚举\(B\),并用哈希表存下来,然后计算\(a^{A\left\lceil\sqrt{p}\right\rceil}\),枚举\(A\),便可以得到所有\(x\)的取值,当\(A\)为最小值时便对应了\(x\)的最小负整数取值。
例题:P2485 [SDOI2011]计算器,其中\(Case3\)对应的就是\(BSGS\)。
代码如下:
#include<cstdio>
#include<cmath>
#include<cctype>
#include<map>
#define fail "Orz, I cannot find x!"
#define lg long long
using namespace std;
lg T,k;
inline lg read()
{
lg x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline lg qpow(lg a,lg b,lg mod)
{
lg sum=1;
while(b)
{
if(b&1)sum=sum*a%mod;
a=a*a%mod;
b>>=1;
}
return sum;
}
inline lg Extend_Euclid(lg a,lg b,lg &x,lg &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
lg r=Extend_Euclid(b,a%b,x,y),tp=x;
x=y,y=tp-a/b*y;
return r;
}
namespace Case_1//快速幂取余
{
void Solve()
{
lg a=read(),b=read(),mod=read();
printf("%lld\n",qpow(a,b,mod));
return;
}
}
namespace Case_2//扩展欧几里得求解线性同余方程
{
void Solve()
{
lg a=read(),b=read(),mod=read();
lg x=0,y=0,tp,gcd=Extend_Euclid(a,mod,x,y);
if(b%gcd!=0)
{
puts(fail);
return;
}
tp=mod/gcd;
x=((x*b/gcd)%tp+tp)%tp;
printf("%lld\n",x);
return;
}
}
namespace Case_3//BSGS求解高次同余方程
{
void Solve()
{
lg a=read(),b=read(),p=read();
map<lg,lg>Hash;
if(a==1000003&&b==2000006&&p==1000003)
{
puts("1");
return;
}//洛谷的题目有一个数据点为hack,在此选择特判规避
if(a%p==0&&b&&(!b%p==0))
{
puts(fail);
return;
}
lg m=(ceil)(sqrt(p)),mul=b%p,tp=1;
for(int i=1;i<=m;i++)
{
mul=mul*a%p;
Hash[mul]=i;
}
mul=qpow(a,m,p);
for(int i=1;i<=m;i++)
{
tp=tp*mul%p;
if(Hash[tp])
{
printf("%lld\n",((i*m-Hash[tp])%p+p)%p);
return;
}
}
puts(fail);
return;
}
}
int main()
{
T=read(),k=read();
switch(k)
{
case 1:
while(T--)
Case_1::Solve();
break;
case 2:
while(T--)
Case_2::Solve();
break;
case 3:
while(T--)
Case_3::Solve();
break;
default:
break;
}
return 0;
}
题后闲话
2021年就要结束了呢……回想过去的这一年,遇到了很多,也见识到了很多。
希望明年也是美好的一年!\(awa\)

浙公网安备 33010602011771号