浅谈BSGS

用于求解形如\(a^x≡b\mod p\)的最小非负整数解\(x\).

由欧拉定理\(a^{\phi(p)}≡1\mod p\)可以知道,我们找的解如果有解则一定在\(\phi(p)\)范围内,而最大的范围就是当\(p\)为质数时,等于\(p-1\).

一种暴力方法是枚举指数验证。由于\(gcd(a,p)=1\).则\(a\)\(\mod p\)意义下必有逆元。所以,我们考虑分解一下质数的表示形式。

知道最大范围不超过\(p\),所以我们令\(x=i*m-j\),将\(a^{-j}\)移项到左边,变成\(a^{i*m}≡b*a^j\mod p\).这里\(m=\lceil{\sqrt{p}}\rceil\).

我们枚举\(b*a^j\),把它们\(\mod p\)的值插入到\(\text{Hash}\)表里面。我们再枚举\(a^{i*m}\),如果这个值在\(\text{Hash}\)表里面出现过,则跳出循环,输出答案。

特判无解,一是没有找到解,另一个是无解,即\(a\mod p=0,b!=0\),或是\(a=0\)的时候。(就那几个特殊情况)

这就是\(BSGS\)的基本思路。下面是\(\text{fast_power,Exgcd,BSGS}\)的三合一模板。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
int mul(int a,int b,int p){return (1ll*a*b)%p;}
int add(int a,int b,int p){return (1ll*a+b)%p;}
int qpow(int a,int b,int p){
	int res=1;
	while(b){
		if(b&1)res=mul(res,a,p);
		a=mul(a,a,p);b>>=1;
	}
	return res;
}
int Exgcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return a;}
	int res=Exgcd(b,a%b,x,y);
	int tmp=x;x=y;y=tmp-a/b*y;
	return res;
}
map<ll,ll>mp;
int T,opt;
signed main(){
	scanf("%lld%lld",&T,&opt);
	for(;T;--T){
		int y,z,p;
		scanf("%lld%lld%lld",&y,&z,&p);
		if(opt==1)printf("%lld\n",qpow(y,z,p));
		else if(opt==2){
			int x,yy;
			int G=Exgcd(y,p,x,yy);
			if(z%G){puts("Orz, I cannot find x!");continue;}
			int tmp=p/G;while(x<0)x+=tmp;
			printf("%lld\n",((x*z/G)%tmp+tmp)%tmp);
		}
		else{
			mp.clear();
			if(y%p==0&&z){puts("Orz, I cannot find x!");continue;}
			int m=ceil(sqrt(p)),f=qpow(y,m,p),now=z%p;
			for(int i=1;i<=m;++i)now=mul(now,y,p),mp[now]=i;
			now=1;int flag=1;
			for(int i=1;i<=m;++i){
				now=mul(now,f,p);
				if(mp[now]){
					int ans=add(mul(i,m,p),-mp[now],p);
					printf("%lld\n",add(ans,p,p));
					flag=0;break;
				}
			}
			if(flag)puts("Orz, I cannot find x!");
		}
	}
	return 0;
} 

\(\text{Hash}\)使用了\(\text{map}\).

时间复杂度\(O(\sqrt{\phi(p)})\),非扩展\(BSGS\)适用于\(p\)为质数情况。\(gcd(p,a)=1\).

posted @ 2020-04-12 19:09  Refined_heart  阅读(121)  评论(0编辑  收藏  举报