\(BSGS(baby-step giant-step)\),即大步小步算法。经常使用于求解离散对数问题。算法
形式化地说,该算法能够在 \(O \sqrt{p}\) 的时间内求解优化
其中 \(gcd(a,p)=1\)ui
根据欧拉定理,当 \(gcd(a,p)=1\) 时,\(a^{\varphi(p)}mod\ p=1\)spa
而 \(a^0mod\ p=1\)code
因此从 \(1 \sim \varphi(p)\) 出现了一个循环节get
所以,咱们只须要求出 \(a^1 \sim a^{\varphi(p)}\)string
而后分别带入左右两边判断是否相等it
通常的题目中 \(p\) 为质数,因此 \(\varphi(p)=p-1\)io
通常就用 \(p\) 替代模板
可是这样作时间复杂度过高,能够用分块的思想优化
令 \(a^x=a^{km-t}\),那么原式就变成了
咱们只须要分别求出 \(t=1 \sim m\) 时 \(ba^t\) 的值,将其存到哈希表里
而后从 \(1 \sim p/m\) 枚举 \(k\) ,判断哈希表中有没有和 \(a^{km}\) 相等的数
当 \(m=\sqrt{p}\) 时最优
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=1e5+5,mod=1e5+3; int p,a,n,blo,h[maxn],tot=1; struct asd{ int nxt,val,num; }b[maxn]; void ad(int val,int id){ rg int now=val%mod; for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int cs=b[i].val; if(cs==val){ if(b[i].num<id) b[i].num=id; return; } } b[tot].val=val; b[tot].nxt=h[now]; b[tot].num=id; h[now]=tot++; } int cx(int val){ rg int now=val%mod; for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int cs=b[i].val; if(cs==val) return b[i].num; } return -1; } int main(){ memset(h,-1,sizeof(h)); p=read(),a=read(),n=read(); blo=sqrt(p)+1;//不要忘了加上一个1保证正确性 rg int now=n,cs=1,haha; for(rg int i=1;i<=blo;i++){ now=1LL*now*a%p; ad(now,i); cs=1LL*cs*a%p; } now=1; for(rg int i=1;i<=blo;i++){ now=1LL*now*cs%p; haha=cx(now); if(haha!=-1){ printf("%d\n",i*blo-haha); return 0; } } printf("no solution\n"); return 0; }
求解
其中 \(a,p\) 不必定互质
这个时候就不能再找 \(1 \sim \varphi(p)\) 的循环节了,由于不知足欧拉定理的条件
咱们把式子变一下,能够写成
的形式
根据裴蜀定理,当 \(gcd(a,p)|b\) 时方程有解,不然无解
因此在有解的状况下,咱们能够把方程左右两边同时除以 \(gcd(a,p)\),等号不变
此时原方程变为
把 \(\frac{p}{gcd(a,p)}\) 当作新的 \(p\)
把 \(\frac{b}{gcd(a,p)}\) 当作新的 \(b\)
同时记录一下左边的系数之积 \(\frac{a}{gcd(a,p)}\),并不断地提出 \(a\)
这样在最后必定会出现 \(gcd(a,p)=1\) 的状况,用普通的 \(bsgs\) 便可
不要忘了把提出去的 \(a\) 的系数加上
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=1e5+5,mod=1e5+3; int h[maxn],tot=1; struct asd{ int nxt,val,num; }b[maxn]; void ad(int val,int id){ rg int now=val%mod; for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int cs=b[i].val; if(cs==val){ if(b[i].num<id) b[i].num=id; return; } } b[tot].val=val; b[tot].nxt=h[now]; b[tot].num=id; h[now]=tot++; } int cx(int val){ rg int now=val%mod; for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int cs=b[i].val; if(cs==val) return b[i].num; } return -1; } int bsgs(rg int a,rg int p,rg int n,rg int xs){ memset(h,-1,sizeof(h)); tot=1; rg int blo=sqrt(p)+1; rg int ac1=n,ac2=1,ac3=xs; for(rg int i=1;i<=blo;i++){ ac1=1LL*ac1*a%p; ad(ac1,i); ac2=1LL*ac2*a%p; } for(rg int i=1;i<=blo;i++){ ac3=1LL*ac3*ac2%p; ac1=cx(ac3); if(ac1!=-1) return i*blo-ac1; } return -1; } int gcd(rg int aa,rg int bb){ return bb==0?aa:gcd(bb,aa%bb); } int main(){ rg int a,p,n,haha=1,now,ans,cnt; rg bool jud=0; while(1){ a=read(),p=read(),n=read(); if(a==0 && p==0 && n==0) break; haha=1,jud=0,cnt=0; a%=p,n%=p; if(n==1){ printf("0\n"); } else if(a==0 && n==0){ printf("0\n"); } else if(a==0){ printf("No Solution\n"); } else if(n==0){ while(1){ if(p==1){ printf("%d\n",cnt); break; } now=gcd(a,p); if(now==1 && p!=-1){ printf("No Solution\n"); break; } cnt++,p/=now; } } else { while(1){ now=gcd(a,p); if(n%now!=0){ jud=1; break; } if(now==1){ ans=bsgs(a,p,n,haha); if(ans==-1) jud=1; ans+=cnt; break; } cnt++,p/=now,n/=now; haha=1LL*haha*(a/now)%p; if(haha==n){ ans=cnt; break; } } if(jud){ printf("No Solution\n"); } else { printf("%d\n",ans); } } } return 0; }