这个算法非常傻逼。
PS:我不会Markdown 数学公式只能这么写,敬请谅解。
用来求高次同余方程:a^x=b(mod p)最小的x
通过费马小定理,我们能得知:a^k=a^(k mod (p-1)) (mod p)
当然,前提是gcd(a,p)>1并且p为质数。
证明:
a^(k mod (p-1))=a^(k-m(p-1))
\frac{a^k}{a^{(p-1)^m}}=a^k (mod p)
显然a^(p-1)^m mod p=1,虽然余数没有可除性,但是应该看得出来这个是成立的
然后我们就把x变成i*m-j
a^m^i=b*a^j(mod p)
枚举j,把右式插入map中,因为j越大,i*m-j越小。所以不考虑重复。
然后再枚举i,将左式的结果再map中查找,第一个就是答案.
推荐一道模板题,SDOI2011计算器,正好复习一下扩欧
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,jzsb,y,z,p;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
if(b==0){
x=1,y=0,d=a;
return ;
}
exgcd(b,a%b,d,x,y);
ll t=x;x=y,y=t-(a/b)*y;
}
map<ll,ll> jyk;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
int i;
cin>>t>>jzsb;
while(t--){
scanf("%lld%lld%lld",&y,&z,&p);
if(jzsb==1){
ll res=1;
while(z){
if(z&1)res=(res*y)%p;
y=(y*y)%p;
z>>=1;
}
printf("%lld\n",res);
}if(jzsb==2){
ll d,u,v;
exgcd(y,p,d,u,v);
if(z%d==0){
printf("%lld\n",(u*(z/d)%(p/d)+(p/d))%(p/d));
}else puts("Orz, I cannot find x!");
}if(jzsb==3){
if(gcd(y,p)>1)puts("Orz, I cannot find x!");
else{
jyk.clear();
ll m=ceil(sqrt(p)),res=z%p,w=1;
for(i=0;i<=m;i++){
jyk[res]=i;
res=(res*y)%p;
if(i>=1)w=(w*y)%p;
}
ll h=1;
int flag=0;
for(i=1;i<=m;i++){
h=(h*w)%p;
if(jyk[h]){
printf("%lld\n",((i*m-jyk[h])%p+(p))%(p)),flag=1;
break;
}
}
if(!flag)puts("Orz, I cannot find x!");
}
}
}
return 0;
}
就好了。