第一次寫一個比較高大上的數論題吧~~~雖然要花好長時間才明白一點,但是有收穫吧~~對擴展歐幾里得加深了理解。還認識了高大上的BSGS算法,哈哈~雖然還是一知半解,腦袋太小理解太慢,不易記住
上別人的題解吧~~~
相關連接:
BSGS算法_Baby steps giant steps算法(無擴展)詳解
bzoj2242: [SDOI2011]計算器 && BSGS 算法
擴展歐幾里德算法求解線性同餘方程
http://blog.csdn.net/lwfcgz/article/details/8887906BSGS[bzoj2242][bzoj3122]
關於第三問一些好的解析:(1)baby step giant step,意爲先小步後大步。由費馬小定理可得答案不會超過p,我們可以把答案X看作k*m+i的形式,顯然當m=sqrt(p)時複雜度最優。預處理m以內的hash值存入map(今天發現map真是個好東西), 枚舉k,因爲ni(y^k*m)*z%p=y^i%p,噹噹前值存在於哈希表是,則說明找到一個合法k與合法i,計入答案。
此時有一個特殊情況,若當前hash值爲一,即ni(y^k*m)===z(mod p),則i=0;
(2)
第三問高次同餘方程a^x=b(mod n),Baby Step Giant Step算法::
解高次同餘方程a^x=b(mod n) n爲質數
設x=i*m+j m=ceil(sqrt(n))
則a^im*a^j=b(mod n)
==>(a^m)^i*a^j=b(mod n)
求a^m模n的乘法逆元v=a^(n-m-1){
乘法逆元:若ax=1(mod n),則稱a模n的乘法逆元是x
性質:K/x mod n = K*a mod n (將就着看,不是很科學)
證明v=a^(n-m-1)是乘法逆元:{
由費馬小定理得a^(n-1)=1(mod n)
則a^m*a^(n-m-1)=1(mod n)
所以v=a^(n-m-1)
}
}
所以(a^m)^i*a^j=b(mod n)
==>a^j=b*v^i(mod n)
枚舉j=0 ->m-1 將a^j mod n存入hash表
枚舉i=0 ->m-1 每次計算 b*v^i mod n,若計算過程中發現b*v^i mod n在hash表中出現過,返回i*m+j這樣就解完了複雜度O(sqrt(n))當然這是你hash寫得好才行,蒟蒻用map,或者二分判斷,複雜度O(sqrt(n)logn)
複雜度O(sqrt(n)log(n))來自快速冪和hash
(3)
給定y、z、p,計算滿足yx mod p=z的最小非負整數x。p爲質數(沒法寫數學公式,以下內容用心去感受吧)
設 x = i*m + j.
則 y^(j)≡z∗y^(-i*m)) (mod p)
則 y^(j)≡z∗ine(y^(i*m)) (mod p)(逆元)
由費馬小定理y^(p-1)≡1 (mod p) 得 ine(y^m) = y^(p-m-1)
ine(y^(i*m)≡ine(y^((i−1)m))∗y^(p-m-1)
1.首先枚舉同餘符號左面,用一個hash保存(y^j,j),因爲j可能等於0,所以hash[1]要賦爲一個特殊值。
2.再枚舉同餘符號右面,如果hash(z∗ine(y^(i*m)))存在,就找到了一組解。
顯然,m=sqrt(p)的時候複雜度最低爲O(sqrt(p)),m=ceil(sqrt(p)).
革命尚未成功,同志仍需努力!
#include<cstdio>
#include<iostream>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
typedef long long ll;
map<ll,ll>hash;
ll y,z,p;
ll power(ll a,ll b,ll mod){
ll res=1;
while(b){
if(b&1) res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
ll res,t;
if(b==0){
x=1;y=0;
return a;
}
res=exgcd(b,a%b,x,y);
t=x;x=y;y=t-(a/b)*y;
return res;
}
ll solve1(){
cout<<power(y,z,p)<<endl;
}
ll solve2(){
ll xx,yy,ans,d;
d=exgcd(y,p,xx,yy);
if(z%d!=0)
cout<<"Orz, I cannot find x!"<<endl;
else{
ans=(z/d)*xx;
ans=(ans%p+p)%p; //求出的ans可能爲負數,這裏求出的是最小的非負解
cout<<ans<<endl;
}
}
ll solve3(){
y%=p;z%=p;
if(!y&&!z)
cout<<"1"<<endl;
else if(!y){
cout<<"Orz, I cannot find x!"<<endl;
}
else{
ll m,v,e,res;
m=ceil(sqrt(p));v=power(y,p-m-1,p);e=1;
hash.clear();
hash[1]=m+1; //特殊情況
for(ll i=1;i<=m;i++){
e=e*y%p;
if(!hash[e])
hash[e]=i;
}
res=-1;
for(ll i=0;i<=m;i++){
if(hash[z]){
res=i*m+(hash[z]==m+1?0:hash[z]);
break;
}
z=z*v%p;
}
if(res==-1)
cout<<"Orz, I cannot find x!"<<endl;
else
cout<<res<<endl;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t,k;
cin>>t>>k;
for(int i=0;i<t;i++){
cin>>y>>z>>p;
//cout<<y<<z<<p;
if(k==1) solve1();
else if(k==2) solve2();
else solve3();
}
}