每组数据输出一行,表示解,如果不存在,则输出-1。
分析:
对于给出的方程 a^x≡b(mod n)
可以把x分解为i*m-j: a^(i*m-j)≡b(mod n)
同乘a^j: a^(i*m)≡b*a^j(mod n)
令m=√n,j∈[0,m],那么先一遍 j=0~m,求出 b*a^j mod n 的结果,j 存入哈希表中。
然后一遍 i=1~m,求出a^(i*m) mod n的结果,并在哈希表中查找是否有结果相同的 j,如果找到,根据上面的式子,x=i*m-j。此时的x一定是最小的。
否则,返回-1,无解。
于是在√n的时间内求出了答案。
有其他版本的做法,但很多是i*m+j,这样需要求逆元。i*m-j的做法避免了求逆元。
注意:
1,哈希表的大小,大概开到2*m就可以。
2,j和i的范围,一点都不能错。
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAXN=87719;
int last[MAXN],np;
struct data{LL num,id;int pre;}Hash[MAXN];
void insert(LL num,LL id)
{
LL hs=num%MAXN;
Hash[++np]=(data){num,id,last[hs]};
last[hs]=np;
}
LL find(LL num)
{
LL hs=num%MAXN;
for(int p=last[hs];p;p=Hash[p].pre)
{
if(Hash[p].num==num)
return Hash[p].id;
}
return -1;
}
LL qkpow(LL a,LL b,LL n)
{
LL s=1;
while(b)
{
if(b&1) s=(s*a)%n;
a=(a*a)%n;
b=b>>1;
}
return s;
}
LL bsgs(LL a,LL b,LL n)
{
memset(last,0,sizeof(last));
memset(Hash,0,sizeof(Hash));
np=0;
LL m=ceil(sqrt(n));
LL aa=qkpow(a,m,n);
LL i;
LL e=b%n;
for(i=0;i<=m;i++)
{
insert(e,i);
e=(e*a)%n;
}
e=1;
for(i=1;i<=m;i++) // 每次乘a^m
{
e=(e*aa)%n;
if(find(e)>=0) //如果hash表里找到了
{
LL ans=i*m-find(e); //i*m-j
return (ans%n+n)%n;
}
}
return -1;
}
int main()
{
// freopen("in.txt","r",stdin);
LL a,b,n;
while(scanf("%lld",&a)==1)
{
scanf("%lld%lld",&b,&n);
LL ans=bsgs(a,b,n);
printf("%lld\n",ans);
}
return 0;
}