Giant Step-Astronauts(May'n・椎名慶治)
閱讀時可以聽聽這兩首歌,加深對這個算法的理解。(Baby steps少女時代翻唱過,這個原唱反而不是很有名……Giant Step就比較碉,是一個假面騎士片的插曲,由超碉的May'n和一個人建立的臨時組合唱的,怕不怕)
這個主要是用來解決這個題:
A^x=B(mod C)(C是質數),都是整數,已知A、B、C求x。
我在網上看了好多介紹,覺得他們寫得都不夠碉,我看不懂…於是我也來寫一發。
先把x=i*m+j,其中m=ceil(sqrt(C)),(ceil是向上取整)。
這樣原式就變為A^(i*m+j)=B(mod C),
再變為A^j=B*A^(-m*i) (mod C),
先循環j=0~(C-1),把(A^j,j)加入hash表中,這個就是Baby Steps(現在可以播放Baby Steps-Varsity了)。
下面我們要做的是枚舉等號右邊,從hash表中找看看有沒有,有的話就得到了一組i j,x=i*m+j,得到的這個就是正確解。
所以,接下來要解決的就是枚舉B*A^(-m*i) (mod C)這一步(這就是Giant Step,快放Giant Step-Astronauts(May'n・椎名慶治))。
A^(-m*i)相當於1/(A^(m*i)),里面有除法,在mod里不能直接用除法,這時候我們就要求逆元。
/*百度百科:
對xA+yB=z,
變成這樣xA = z - yB,取B=C(C就是我們要mod的那個)
推導出 xA % C = z %C
只要 z%C==1 時,就可以求出A的逆元x
但用exgcd求完,x可能是負數,還需要這樣一下:x=(x%C+C)%C
//--exgcd介紹完畢--
再看我們的題目,
exgcd(A^(m*i) , C)=z,當C是質數的時候z肯定為1,這樣exgcd求得的x就是逆元了。
因為x就是A^(m*i)的逆元,P/(A^(m*i))=P*x,所以
B*A^(-m*i) = B/(A^(m*i)) = B*x(mod C)
這樣我們的式子A^j=B*A^(-m*i) (mod C)的等號右邊就有了,就是B*x,就問你怕不怕!
枚舉i,求出右邊在hash里找,找到了就返回,無敵!
例題:
http://poj.org/problem?id=2417
代碼:

1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<map> 8 #include<set> 9 using namespace std; 10 #define ll __int64 11 #define usint unsigned int 12 #define RE freopen("1.in","r",stdin) 13 14 class hash { 15 public: 16 hash() { 17 memset(a,0xff,sizeof(a)); 18 } 19 int locate(ll x) { 20 ll l=x%MOD; 21 while(a[l]!=x&&a[l]!=-1) l=(l+1)%MOD; 22 return l; 23 } 24 void insert(ll x,ll va) { 25 ll l=locate(x); 26 if(a[l]==-1) { 27 a[l]=x; 28 v[l]=va; 29 } 30 } 31 ll get(ll x) { 32 ll l=locate(x); 33 return a[l]==x?v[l]:-1; 34 } 35 void clear() { 36 memset(a,0xff,sizeof(a)); 37 } 38 private: 39 static const ll MOD=100007; 40 ll a[MOD+100],v[MOD+100]; 41 } S; 42 43 ll exgcd(ll a,ll b,ll &x,ll &y) { 44 ll t,ret; 45 if (!b) { 46 x=1,y=0; 47 return a; 48 } 49 ret=exgcd(b,a%b,x,y); 50 t=x,x=y,y=t-a/b*y; 51 return ret; 52 } 53 54 int main() {///A^x=B(modC) A^j=B*A^(-m*i)(mod C) 55 ll C,A,B; 56 ll m,i,t,D,ans,x,y; 57 while(scanf("%lld%lld%lld",&C,&A,&B)!=EOF) { 58 S.clear(); 59 m=ceil(sqrt((double)C)); 60 t=1; 61 for(i=0; i<m; i++) { /**One, two, baby steps.Three, four, baby steps.Five, six, baby steps.**/ 62 S.insert(t,i); 63 t=t*A%C; 64 } 65 D=1;///此時t=A^m 66 ans=-1; 67 for(i=0; i<m; i++) { /**一歩 Giant Step , 君にとってLittle だとしても**/ 68 exgcd(D,C,x,y);///exgcd求逆元,得到x=D^(-i*m) 69 x=((x*B)%C+C)%C;///B*x=B*D^(-i*m) 70 y=S.get(x); 71 //printf("%lld,%lld\n",x,y); 72 if(y!=-1) { 73 ans=i*m+y; 74 break; 75 } 76 D=(D*t)%C;///D=t^i,(t=A^m) 77 } 78 if(ans==-1) printf("no solution\n"); 79 else printf("%lld\n",ans); 80 } 81 return 0; 82 }