BSGS算法
BSGS算法,即大小步算法(Baby-Step-Giant-Step)
俗称北上广深、拔山盖世算法。
用途
可以在$O(\sqrt{P})$的时间负复杂度求出形如$A^x\equiv B ( mod P ),(Gcd(P,A)=1)$的方程的最小非负整数解。
实现
根据费马小定理,我们不难证明若最小非负整数$x$存在,那么一定有$x<P$,那么我们不妨假设$x=kn-m,A^{kn-m}\equiv B$,其中$n$为$\sqrt{P}$向上取整,$k、m$为不超过$n$的正整数。
这样做有什么好处呢?我们发现$k、m$都是取值范围小于$\sqrt{P}$的正整数,我们想要利用好这条性质,就要把式子变一下形:
$$ A^{kn-m}\equiv B ( mod P )$$
$$ A^{kn}\equiv A^{m}\cdot B ( mod P )$$
我们令$G=A^n( mod P )$,则
$$ G^k\equiv A^{m}\cdot B ( mod P )$$
这样,我们就可以先处理处每一个$ A^{m}\cdot B $,存进哈希表中,再求出$G=A^n$,并依次枚举$ G^k $在哈希表中是否存在,优先取$k$小的即可,最终就有$x=kn-m$。
扩展
不难看出,以上过程依托费马小定理限定了$x$的取值范围,所以限制是$(Gcd(P,A)=1)$,但如果不是这样呢?
我们不妨假设$(Gcd(P,A)=d),A=a\times d,B=b\times d,P=p\times d$(如果$B$不是$d$的倍数则方程在整数范围内无解)
则:
$$(a\cdot d)^x \equiv b\cdot d (mod (p\cdot d))$$
根据等式的性质,一定有
$$ a\cdot (a\cdot d)^{x-1}\equiv b (mod p)$$
$$ (a\cdot d)^{x-1}\equiv b\cdot a^{-1} (mod p)$$
依照上文求解即可
附上SDOI2016计算器的代码
大家不要在意我丧心病狂的写了个trie树当哈希用...
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
#define mid (l+r>>1)
#define M 1020000
using namespace std;
LL read(){
LL nm=0,fh=1;char cw=getchar();
for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return nm*fh;
}
LL m,mod,T,tpe,k,tmp,p[M][10],node,hv0,cnt=1,tg[M];
LL qpow(LL x,LL qs){
LL fin=1;
while(qs){
if(qs&1) fin=fin*x%mod;
x=x*x%mod,qs>>=1;
}
return fin;
}
void ins(LL x,LL id){
if(x==0){hv0=max(id,hv0);return;}
LL now=1;
while(x>0){
if(p[now][x%10]==0){
p[now][x%10]=++cnt;
tg[cnt]=-1;
for(LL i=0;i<10;i++) p[cnt][i]=0;
}
now=p[now][x%10],x/=10;
}
tg[now]=max(tg[now],id);
}
LL find(LL x){
if(x==0) return hv0;
LL now=1;
while(x>0){
if(p[now][x%10]==0) return -1;
now=p[now][x%10],x/=10;
}
return tg[now];
}
void init(int x){
if(x==0) return;
for(int i=0;i<=9;i++) init(p[x][i]),p[x][i]=0;
tg[x]=-1;
}
void getans(){
if(tpe==1){printf("%lld\n",qpow(k,m));return;}
if(tpe==2){
if((k%mod==0)^(m%mod==0)) puts("Orz, I cannot find x!");
else printf("%lld\n",m*qpow(k,mod-2)%mod);
return;
}
init(1),m%=mod,k%=mod,hv0=-1;
if((k==0)^(m==0)){puts("Orz, I cannot find x!");return;}
LL n=sqrt(mod)+1,u;
u=qpow(k,n),cnt=1;
for(LL i=1,now=k;i<=n;i++,now=now*k%mod) ins(now*m%mod,i);
for(LL i=1,now=u;i<=n;i++,now=now*u%mod){
LL tk=find(now);
if(tk==-1) continue;
printf("%lld\n",i*n-tk);
return;
}
puts("Orz, I cannot find x!");
}
int main(){
hv0=-1,T=read(),tpe=read(),memset(tg,-1,sizeof(tg));
while(T--){k=read(),m=read(),mod=read(),getans();}
return 0;
}