关于BSGS算法

BSGS算法是一种在解决模线性同余方程ax ≡ b (mod p)时的有效方法,尤其当p为素数。通过转换,将原问题转化为等比数列的形式,能在O(sqrt(p))的时间复杂度内求解。该算法还能应用于处理等比数列的求解。文章提到了特殊情况的处理,如x1 + (a-1)b = 0时的逆元问题,并提供了一个SDOI 2013比赛中的实际应用题目及AC代码示例。

关于BSGS算法

文章中提到的p,如果不加说明则默认为素数。
BSGS算法适用于在求解形如ax≡b(modp)a^x\equiv b \pmod paxb(modp)的不等式。

可知0≤x<p0\le x<p0x<p,那么可以令x=A⌈p⌉−B,0<=A,B<=⌈p⌉x=A\lceil\sqrt{p}\rceil-B,0<=A,B<=\lceil\sqrt{p}\rceilx=ApB,0<=A,B<=p
所以原问题转换成
aA⌈p⌉−B≡b mod pa^{A\lceil\sqrt{p}\rceil-B}\equiv b\bmod p\newlineaApBbmodp

aA⌈p⌉≡b∗aB mod pa^{A\lceil\sqrt{p}\rceil}\equiv b*a^B\bmod paApbaBmodp

在O(sqrt(p ))内枚举等式右边的值并储存到哈希表中,然后在O(sqrt(p ))内枚举等式左边的值并在哈希表内查询,等式成立,则有x=A⌈p⌉−Bx=A\lceil\sqrt{p}\rceil-Bx=ApB$。

BSGS算法也可以处理形如xi+1≡a∗xi+b(modp),求t≡xansx_{i+1}\equiv a*x_i+b \pmod{p} , 求t\equiv x_{ans}xi+1axi+b(modp)txans
之类的问题,只需将上述数列转换成等比数列
xi+1+ba−1≡a∗(xi+ba−1)(modp),a>1x_{i+1}+\frac{b}{a-1}\equiv a*(x_i+\frac{b}{a-1}) \pmod{p},a>1 xi+1+a1ba(xi+a1b)(modp),a>1
那么
xn+ba−1≡an−1(x1+ba−1)(modp)x_{n}+\frac{b}{a-1}\equiv a^{n-1}(x_1+\frac{b}{a-1}) \pmod{p} xn+a1ban1(x1+a1b)(modp)
因为要求t≡xanst\equiv x_{ans}txans ,所以上述等式可以转化为
(t+ba−1)∗rev(x1+ba−1)≡aans−1(modp)(t+\frac{b}{a-1})*rev(x_1+\frac{b}{a-1})\equiv a^{ans-1} \pmod{p} (t+a1b)rev(x1+a1b)aans1(modp)
只有ans是未知的,所以转化为文章开头所讲的问题。

要注意如果x1+ba−1=0x_1+\frac{b}{a-1}=0x1+a1b=0的话,则没有逆元。这种情况同a<1的情况需要特殊处理。

当然BSGS算法经过一些改动可以处理p不为素数的情况,但本文不敞开论述。

附上一道题目
SDOI 2013(随机数生成器)
AC代码如下:

#include<bits/stdc++.h>//注意0不在乘法群中,因为0元没有逆元 
using namespace std;
const int max_n=1e6;
const int HashMod=1e5;
typedef long long ll;
struct Hash_Map{
	struct Line{int k;int v;int next;}e[max_n];
	int HashMap[HashMod],cnt;
	void init(void)
	{
		memset(HashMap,-1,sizeof(HashMap));
		cnt=0;
	}
	void add(int key,int value,int hashkey)
	{
		e[cnt++]=Line{key,value,HashMap[hashkey]};
		HashMap[hashkey]=cnt-1;
	}
	void insert(int key,int value)
	{
		add(key,value,key%HashMod);
	}
	int find(int key)
	{
		for(int i=HashMap[key%HashMod];i!=-1;i=e[i].next)
		if(e[i].k==key)return e[i].v;
		return -1;
	}
}mp;
ll qpow(ll a,ll n,ll p)
{
	ll ans=1;
	while(n)
	{
		if(n&1)ans=ans*a%p;
		a=a*a%p;
		n>>=1;
	}
	return ans;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b){
		x=1,y=0;return ;
	}
	else{
		exgcd(b,a%b,y,x);y-=(a/b)*x;return ;
	}
}
ll rev(ll a,ll p)
{
	ll t,t0;
	exgcd(a,p,t,t0);
	return (t+p)%p;
}
ll BSGS(ll a,ll b,ll p)
{
	a%=p;b%=p;
	if(b==1)return 0;
	mp.init();
	ll m=ceil(sqrt(p));
	for(ll i=0,ret=b;i<=m;i++)
	{
		mp.insert(ret,i);
		ret=ret*a%p;
	}
	ll t=qpow(a,m,p);
	for(ll i=1,ret=t;i<=m;i++)
	{
		int f=mp.find(ret);
		if(f>=0)return i*m-f;
		ret=ret*t%p;
	}
	return -1;
}
int main(void)
{
	freopen("out.txt","w",stdout);
	int t0;
	scanf("%d",&t0);
	ll p,a,b,x,t;
	while(t0--)
	{
		scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x,&t);
		a%=p;b%=p;t%=p;x%=p;
		ll ans;
		if(a==0){
			if(t==x)ans=1;
			else if(t==b)ans=2;
			else ans=-1;
		}
		else if(a==1&&b!=0){
			ans=(((t-x)+p)%p*rev(b,p)%p+1)%p;
			if(!ans)ans=p;
		}
		else if(a==1&&b==0){
			if(x==t)ans=1;
			else ans=-1;
		}
		else if(a>1){
			int temp=b*rev(a-1,p)%p;
			if(x+temp==0){
				if(t==0)ans=0;
				else ans=-1;
			}
			else ans=BSGS(a,(t+temp)%p*rev(x+temp%p,p)%p,p);
			if(ans!=-1)ans+=1;
		}
		printf("%lld\n",ans);
	}
	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值