BSGS学习记录/POJ 3696 The Luckiest number

BSGS

BSGS(baby-step giant-step),即大步小步算法。常用于求解离散对数问题。形式化地说,该算法可以在 \(O(\sqrt{p})\) 的时间内求解

\[a^x \equiv b \pmod p \]

其中 \(a\perp p\)。方程的解 \(x\) 满足 \(0 \le x < p\)。(在这里需要注意,只要 \(a\perp p\) 就行了,不要求 \(p\) 是素数)

算法描述

令 \(x = A \left \lceil \sqrt p \right \rceil - B\),其中 \(0\le A,B \le \left \lceil \sqrt p \right \rceil\),则有 \(a^{A\left \lceil \sqrt p \right \rceil -B} \equiv b \pmod p\),稍加变换,则有 \(a^{A\left \lceil \sqrt p \right \rceil} \equiv ba^B \pmod p\)。

我们已知的是 \(a,b\),所以我们可以先算出等式右边的 \(ba^B\) 的所有取值,枚举 \(B\),用 hash/map 存下来,然后逐一计算 \(a^{A\left \lceil \sqrt p \right \rceil}\),枚举 \(A\),寻找是否有与之相等的 \(ba^B\),从而我们可以得到所有的 \(x\),\(x=A \left \lceil \sqrt p \right \rceil - B\)。

注意到 \(A,B\) 均小于 \(\left \lceil \sqrt p \right \rceil\),所以时间复杂度为 \(\Theta\left (\sqrt p\right )\),用 map 则多一个 \(\log\)。

上述介绍在CC BY-SA 4.0SATA条款下,转自OIWIKI

P3846 [TJOI2007] 可爱的质数

模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<ll,ll> mp;

int main(){
	ll p,a,b;
	scanf("%lld%lld%lld",&p,&a,&b);
	ll sqp=ceil(sqrt(p));//square root of p
	ll rs=b,ap=1,ls=1;//rs/ls: right/left side
	//ap: sqp-th power of a
	for(int i=1;i<=sqp;++i){
		rs=(rs*a)%p;
		ap=(ap*a)%p;
		mp[rs]=i;
	}
	for(int i=1;i<=sqp;++i){
		ls=(ls*ap)%p;// ls=a^{i*\sqrt{p}}
		if(mp[ls]){
			printf("%lld\n",((i*sqp-mp[ls])%p+p)%p);
			return 0;
		}
	}
	puts("no solution");
	return 0;
}

POJ The Luckiest number

求 \(\underbrace{888 \cdots88}_{\text{x个}}\equiv 0\space (mod\space L)最小的符合条件的正整数x\).

即 \((10^x-1) \cdot\frac{8}{9}\equiv0\space(mod\space L)\)

\(9n|8(10^x-1)\)

\(\because gcd(8,9)=1,gcd(\frac{n}{gcd(L,8)},\frac{L}{gcd(L,8)})=1\)

\(\therefore 10^x\equiv1(mod\space \frac{9n}{gcd(L,8)})\)

注意到与此时x应严格大于0,在处理时,不能使 \(x=A \left \lceil \sqrt p \right \rceil - B\) (\(a^{A\left \lceil \sqrt p \right \rceil} \equiv ba^B \pmod p\))的B不能为\(\left \lceil \sqrt p \right \rceil\),第一次循环处理到\(\left \lceil \sqrt p \right \rceil-1\).

时间复杂度\(O(\sqrt L)\)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<utility>
using namespace std;
typedef long long ll;
#define N 150000
ll hs[N],nxt[N],hd[N],val[N];
ll cnt;

void ins(ll x,ll v){
	ll p=x%N;
	hs[++cnt]=x;
	val[cnt]=v;
	nxt[cnt]=hd[p];
	hd[p]=cnt;
}

ll fd(ll x){
	ll p=x%N;
	for(ll i=hd[p];i!=-1;i=nxt[i]){
		if(hs[i]==x){
			return val[i];
		}
	}
	return -1;
}

ll __gcd(ll a,ll b){
	return b==0?a:__gcd(b,a%b);
}

ll mul(ll a,ll b,ll c){
	if(a<b){
		swap(a,b);
	}
	ll ret=0,tmp=a;
	while(b){
		if(b&1){
			ret+=tmp;
			if(ret>=c) ret-=c;	
		}
		tmp<<=1;
		if(tmp>=c) tmp-=c;
		b>>=1;
	}	
	return ret%c;
}

int main(){
	ll n;
	ll cs=0;
	while(scanf("%lld",&n),n){
		cnt=0;
		memset(hd,-1,sizeof hd);
		ll p=9*n/__gcd(n,8ll),a=10,b=1;
		if(__gcd(p,a)!=1){
			printf("Case %lld: 0\n",++cs);
			continue;
		}
		ll sqp=ceil(sqrt((double)p));//square root of p
		ll rs=b,ap=1,ls=1;//rs/ls: right/left side
		//ap: sqp-th power of a
		for(ll i=1;i<sqp;++i){
			rs=(rs*a)%p;
			ap=(ap*a)%p;
			ins(rs,i);
		}
		if(sqp) ap=(ap*a)%p;
		bool f=0;
		for(ll i=1;i<=sqp;++i){
			ls=mul(ls,ap,p);// ls=a^{i*\sqrt{p}}
			ll j;
			if((j=fd(ls))!=-1){
				f=1;
				printf("Case %lld: %lld\n",++cs,((i*sqp-j)%p+p)%p);
				break;
			}
		}
		if(!f){
			printf("Case %lld: 0\n",++cs);
		}
	}
	return 0;
}

另外本题还有另一种解法

易证:\(a\perp n, a^x \equiv 1(mod\space n)\)的最小整数解使\(\varphi(n)\)的约数,枚举约数即可。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<utility>
#include<iostream>
using namespace std;
typedef long long ll;

ll __gcd(ll a,ll b){
	return b==0?a:__gcd(b,a%b);
}

ll mul(ll a,ll b,ll c){
	if(a<b){
		swap(a,b);
	}
	ll ret=0,tmp=a;
	while(b){
		if(b&1){
			ret+=tmp;
			if(ret>=c) ret-=c;	
		}
		tmp<<=1;
		if(tmp>=c) tmp-=c;
		b>>=1;
	}	
	return ret%c;
}

ll qpow(ll a,ll b,ll c){
	ll ret=1,tmp=a;
	while(b){
		if(b&1){
			ret=mul(ret,tmp,c);
		}
		tmp=mul(tmp,tmp,c);
		b>>=1;
	}
	return ret%c;
}

int main(){
	ll n;
	ll cs=0;
	while(scanf("%lld",&n),n){
		ll p=9*n/__gcd(n,8ll);
		if(__gcd(p,10)!=1){
			printf("Case %lld: 0\n",++cs);
			continue;
		}
		ll fai=p,t=p,r=sqrt(p);
		for(ll i=2;i<=r;++i){
			if(t%i==0){
				fai=fai/i*(i-1);
				t/=i;
				while(t%i==0){
					t/=i;
				}	
			}
		}
		if(t>1){
			fai=fai/t*(t-1);
		}
		r=sqrt(fai);
		ll ans=1e18;
		bool f=0;
		for(ll i=1;i<=r;++i){
			if(fai%i==0){
				if(qpow(10,i,p)==1){
					ans=min(ans,i);
					f=1;
				}
				if(qpow(10,fai/i,p)==1){
					ans=min(ans,fai/i);
					f=1;
				}
			}
		}
		printf("Case %lld: %lld\n",++cs,f?ans:0);
	}
	return 0;
}
上一篇:查找之——B树、B+树的概念及性质+答题技巧(必背几句,考试不慌,稳得一匹)


下一篇:Codeforces 1329 题解