BSGS算法

一,定义

bsgs可以在O(\sqrt{q})时间内求解出a^{x}\equiv b (mod \ p)问题。要求a,p互质(p为质数)

二,思想

  1. 由费马小定理,a^{p-1}=a,显然至多枚举0~p-1次遍可知道答案
  2. 我们可以用i*m-j=x来表示x(m为待定常数,i,j为变量。0<=j<=m,1<=m<=p/m)。显然原式等于a^{i*m}=b*a^{j}(mod \ p)
  3. 如果我们把右式全部枚举并存入哈希表,那么左边只需要遍历i询问哈希表中是否存在a^{i*m}(mod \ p)即可。复杂度是O(max(m,p/m)),当且仅当m=\left \lceil O(\sqrt{q}) \right \rceil时最优。
  4. 因为枚举时可能出现i*m-j>p的情况,所以如果是求p次数内可以到达的所有x,那么需要保证i*m-j<p,否则超出p后被取模会得到错误结果。

三,模板题:P3846 [TJOI2007] 可爱的质数

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
void mysolve()
{
	int a,b,mod;
	cin>>mod>>a>>b;
	if(__gcd(a,mod)!=1)//首先要满足a,p互质
		{
			cout<<"no solution"<<endl;
			return;
		}
	int m=ceil(sqrt(mod));//m向上取整
	unordered_map<int,int>mp;//哈希表
	int now=1;
	for(int i=0; i<=m; ++i)//哈希存入右边式子,0<=j<=m
		{
			if(i)now=now*a%mod;//now表示a的i次幂
			mp[b*now%mod]=i;//这里有乘法记得取模
		}
	int ans=1;
	for(int i=1; i<=m; ++i)
		{
			ans=ans*now%mod;//ans表示a^m的i次幂
			if(mp.count(ans)&&i*m-mp[ans]<mod)
				{
					cout<<(i*m-mp[ans]+mod)%mod<<endl;//因为从小到达枚举,所以第一个就是最小
					return;
				}
		}
	cout<<"no solution"<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

 例题:P3306 [SDOI2013] 随机数生成器 - 洛谷 

思路:

  1. 相邻xi有公式x[i+1]=a*x[i]+b\Leftrightarrow x[i+1]+\frac{b}{a-1}=a*(x[i]+\frac{b}{a-1})即等比数列

  2. 所以目标 t+\frac{b}{a-1}=x[n]+\frac{b}{a-1}=a^{n-1}*(x[1]+\frac{b}{a-1})\Leftrightarrow a^{n-1}=\frac{t+\frac{b}{a-1}}{x[1]+\frac{b}{a-1}}转化成了bsgs裸题

  3. 但是注意到a=0与a=1需要特判处理,不满足上式

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
int p,a,b,x,t;

ll fastmi(int base,int power)
{
	ll ans=1;
	while(power)
		{
			if(power&1)ans=ans*base%p;
			base=base*base%p;
			power>>=1;
		}
	return ans;
}

void mysolve()
{
	cin>>p>>a>>b>>x>>t;
	if(x==t)
		{
			cout<<1<<endl;
			return;
		}
	if(a==0)
		{
			if(b==t)cout<<2<<endl;
			else cout<<-1<<endl;
			return;
		}
	else if(a==1)
		{
			if(!b)cout<<-1<<endl;
			else cout<<(t-x+p)%p*fastmi(b,p-2)%p+1<<endl;
			return;
		}
	int m=ceil(sqrt(p));
	int now=1,tmp=b*fastmi(a-1,p-2)%p;
	b=(t+tmp)*fastmi(x+tmp,p-2)%p;
	unordered_map<int,int>mp;
	for(int i=0; i<=m; ++i)//j从0到m
		{
			if(i)now=now*a%p;
			mp[b*now%p]=i;
		}
	int ans=1;
	for(int i=1; i<=m; ++i)
		{
			ans=ans*now%p;
			if(mp.count(ans)&&i*m-mp[ans]<p)
				{
					cout<<(i*m-mp[ans]+p)%p+1<<endl;
					return;
				}
		}
	cout<<-1<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值