BSGS exBGS
BSGS
求离散对数,即给出\(a,b,p\),求最小非负整数\(x\)满足:
\[a^x \equiv b \ (mod\ p)
\]
其中\(gcd(a,p)=1\)。
令\(y=\sqrt{p}\)。
1.把\(a\)的\(0...y-1\)次幂\(*b\)塞到哈希表里。
2.枚举\(i\),看下是否存在\(a^{iy-j}\equiv b\ (mod\ p)\):
即\(a^{iy}\equiv a^jb\ (mod\ p)\)
\(j\in [0,y-1]\),所以在哈希表里查一下就行了。
exBSGS
不保证\(gcd(a,p)=1\)。
如果\(gcd(a,p)\nmid b\)则无解(写成方程就知道了)。
否则
\[a^{x-1}\frac{a}{d} \equiv \frac{b}{d} \ (mod\ \frac{p}{d})
\]
如果变成\(a^x=0\)就返回除的次数;如果\(gcd(a,p)=1\)就变成前面带个系数的\(BSGS\),也就\(a^iy\)变成\(Aa^iy\)而已。
洛谷模板题
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
namespace Hash_Table{
const int md=19260817;
int T,tot,hd[md],nxt[100010],num[100010],val[100010],tim[100010];
void add(int x,int y){
int i=x%md;
if (tim[hd[i]]<T) hd[i]=0;
nxt[++tot]=hd[i],hd[i]=tot;
num[tot]=x,val[tot]=y;
tim[tot]=T;
}
int qry(int x){
int i=x%md;
if (tim[hd[i]]<T) return -1;
for (int j=hd[i];j;j=nxt[j])
if (num[j]==x) return val[j];
return -1;
}
}
int power(int x,int p,int md){
int ret=1;
for (;p;p>>=1,x=1ll*x*x%md)
if (p&1) ret=1ll*ret*x%md;
return ret;
}
int phi(int n){
int ret=n;
for (int i=2;i*i<=n;++i)
if (n%i==0){
ret=ret/i*(i-1);
for (;n%i==0;n/=i);
}
if (n>1) ret=ret/n*(n-1);
return ret;
}
int gcd(int x,int y){
if (!y) return x;
else return gcd(y,x%y);
}
int bsgs(int a,int b,int p,int A){
++Hash_Table::T,Hash_Table::tot=0;
int y=sqrt(p)+1,num=b,sum;
for (int i=0;i<y;++i,num=1ll*num*a%p) Hash_Table::add(num,i);
num=power(a,y,p),sum=1ll*A*num%p;
for (int i=1,x;i<=y+1;++i,sum=1ll*sum*num%p){
x=Hash_Table::qry(sum);
if (x!=-1) return i*y-x;
}
return -1;
}
int exbsgs(int a,int b,int p){
int ret=0,num=1;
for (int d;;){
d=gcd(a,p);
if (d==1) break;
if (b%d) return -1;
p/=d,b/=d,++ret,num=1ll*num*a/d%p;
if (num==b) return ret;
}
return ret+bsgs(a,b,p,num);
}
int a,b,p;
int main()
{
for (scanf("%d%d%d",&a,&p,&b);a||b||p;){
a%=p,b%=p;
int x=exbsgs(a,b,p);
if (x>=0) printf("%d\n",x);
else puts("No Solution");
scanf("%d%d%d",&a,&p,&b);
}
return 0;
}