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))
(jin [0,y-1]),所以在哈希表里查一下就行了。
exBSGS
不保证(gcd(a,p)=1)。
如果(gcd(a,p) mid 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
",x);
else puts("No Solution");
scanf("%d%d%d",&a,&p,&b);
}
return 0;
}