这个算法非常傻逼。
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; }
就好了。