bzoj2242 : [SDOI2011]計算器 [BSGS算法]

第一次寫一個比較高大上的數論題吧~~~雖然要花好長時間才明白一點,但是有收穫吧~~對擴展歐幾里得加深了理解。還認識了高大上的BSGS算法,哈哈~雖然還是一知半解,腦袋太小理解太慢,不易記住大哭

上別人的題解吧~~~


相關連接:

BSGS算法_Baby steps giant steps算法(無擴展)詳解

BSGS[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();
	}
}



發佈了86 篇原創文章 · 獲贊 38 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章