终于稍微有点空了。。
我们来看这个方程:
a,b,p为常数且在int内。
注意到这次p可以为合数。
先来说说p为质数或者合数有什么问题。
对于a与p互质,那么有a^phi(p)=1(mod p),对于p是素数phi(p)==p-1,所以x的取值只要在0->n-2之中取就可以了.然而如果p为合数,phi(p) < p-1,这个范围不明确,就不好分块了。而且解是否存在,有几个,也很麻烦。
因此有extended-baby-step-giant-step算法。
考虑a与p不互质的情况:
对于上面的方程,我们可以考虑从x个a中拿出c个a与b和p消去公因子,直到a和p’互质为止。
一旦互质了,那么方程就是v*a^(x-c)=b’ (mod p’),v是拿出c个a消去公因子后剩下的东西,b’,p’是消去公因子的b,p。
这个时候还要求v的逆元,方程变为a^(x-c)=b’*v^(-1) (mod p’)。
此时就可以用baby-step-giant-step做了,答案为BSGS的答案+c。
注意:有可能c会大于x,所以必须约去一次就特判一次方程两边(就是v和b’)是不是相等了。如果两边相等那么直接返回c。
附HDU2815EXBSGS裸题代码(hash表乱写的所以很慢)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
ll a,b,p,ans,hsh[70000][2];
bool ishsh[70000];
void insert(ll x,ll t)
{
ll p=x*12580%70000;
while(ishsh[p])p=(p+1)%70000;
ishsh[p]=1;
hsh[p][0]=x;
hsh[p][1]=t;
}
int query(ll x)
{
ll p=x*12580%70000;
while(hsh[p][0]!=x&&ishsh[p])
p=(p+1)%70000;
if(!ishsh[p])return -1;
return hsh[p][1];
}
ll gcd(ll a, ll b)
{
return b?gcd(b,a%b):a;
}
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(!b){x=1;d=a;y=0;}
else
{
exgcd(b,a%b,d,y,x);
y-=a/b*x;
}
}
ll inv(ll a,ll p)
{
ll x,y,d;
exgcd(a,p,d,x,y);
return (x+p)%p;
}
ll BSGS(ll a,ll b,ll p)
{
ll m=ceil(sqrt(p)),d=1,val=1,gcd,x,y,t;
for(int i=0;i<m;++i)
{
insert(val,i);
val=val*a%p;
}
for(int i=0;i<m;++i)
{
exgcd(d,p,gcd,x,y);
x=(b/gcd*x%p+p)%(p/gcd);
t=query(x);
if(t!=-1)return i*m+t;
d=d*val%p;
}
return -1;
}
ll EXBSGS(ll a,ll b,ll p)
{
ll t,c=0,v=1;
while((t=gcd(a,p))!=1)
{
if(b%t)return -1;
p/=t;//p一定要先除
b/=t;
v=v*a/t%p;//因为这里%p是约去后的p
++c;
if(b==v)return c;//特判
}
b=b*inv(v,p)%p;
ll ret=BSGS(a,b,p);
return ret!=-1?ret+c:ret;
}
int main()
{
while(~scanf("%I64d%I64d%I64d",&a,&p,&b))
{
memset(hsh,0,sizeof hsh);
memset(ishsh,0,sizeof ishsh);
if(b>=p)puts("Orz,I can’t find D!");//引号是全角的- -
else if(b==0)puts("0");
else if((ans=EXBSGS(a,b,p))==-1)puts("Orz,I can’t find D!");
else printf("%I64d\n",ans);
}
}