最近在学习数论,然而发现之前学的baby step giant step又忘了,于是去翻了翻以前的代码,又复习了一下。
觉得总是忘记是因为没有彻底理解啊。
注意baby step giant step只能用在b和p互质的情况下,因为只有b和p互质的情况下,b才有mod p下的逆元。(下面要用到逆元)
当b和p不互质,就要处理一下。现在就正在做这么一题,方法以后再写。
求a^(-m)就用到了求逆元了,那么如何求逆元呢?我学了两种方法:
·1:欧拉定理:当a和n互质,a^φ ( n) ≡ 1(mod n)。【φ ( n) 是小于等于n的与n互质的数的个数】
·2:拓展欧几里德:设a在mod n下的逆元是x,则满足:ax ≡ 1(mod n)
即ax+ny=1。(a和n是常数,x和y是未知数,用拓展欧几里德求解即可)
注:只有当a和n互质,a才有mod n下的逆元。
代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 #define LL long long 9 #define Maxn 1000010 10 11 struct node 12 { 13 int idx; 14 LL val; 15 }baby[Maxn]; 16 17 bool cmp(node x,node y) {return x.val!=y.val ? x.val<y.val : x.idx<y.idx;} 18 19 int binsearch(int cnt,LL tmp) 20 { 21 int head=0,tail=cnt; 22 while(head<=tail) 23 { 24 int mid=(head+tail)>>1; 25 if(baby[mid].val==tmp) return baby[mid].idx; 26 if(baby[mid].val<tmp) head=mid+1; 27 else tail=mid-1; 28 } 29 return -1; 30 } 31 32 LL powmod(LL a,LL b,LL mod) 33 { 34 LL ret=1; 35 a%=mod; 36 while(b) 37 { 38 if(b&1) ret=(ret*a)%mod; 39 a=(a*a)%mod; 40 b>>=1; 41 } 42 return ret; 43 } 44 45 int main() 46 { 47 LL p,b,n; 48 while(scanf("%lld%lld%lld",&p,&b,&n)!=EOF) 49 { 50 int m=(int)ceil(sqrt((double)(p-1))),cnt=0; 51 baby[0].idx=0,baby[0].val=1; 52 for(int i=1;i<m;i++) 53 baby[i].idx=i,baby[i].val=(baby[i-1].val*b)%p; 54 sort(baby,baby+m,cmp); 55 for(int i=1;i<m;i++) 56 if(baby[i].val!=baby[cnt].val) baby[++cnt]=baby[i]; 57 LL bm=powmod(powmod(b,p-2,p),m,p); 58 //printf("bm = %lld\n",bm); 59 int ans=-1; 60 LL tmp=n; 61 for(int i=0;i<m;i++) 62 { 63 int pos=binsearch(cnt,tmp); 64 if(pos!=-1) 65 { 66 ans=i*m+pos; 67 break; 68 } 69 tmp=(tmp*bm)%p; 70 } 71 if(ans==-1) printf("no solution\n"); 72 else printf("%d\n",ans); 73 } 74 return 0; 75 }
2016-02-03 09:50:33