BSGS学习笔记
VI.BSGS(大步小步算法)
欢迎来到 北上广深 拔山盖世 比赛搞事 不算个事 算法学习现场。
BSGS
,全名 Baby Step Giant Step
算法,是用于求解 ,其中 的算法。
我们记 。则 即可表示成 ,其中 的形式。
于是 ,则 。于是我们只需预处理出 乘上 的所有 次幂,扔进哈希表中,然后依次枚举 的 次幂的 次幂进行求解即可。
若使用 map
,复杂度为 ;若使用手写哈希表或 unordered_map
,复杂度为 。
VI.I.[TJOI2007] 可爱的质数/【模板】BSGS
代码:
#include<bits/stdc++.h>
using namespace std;
int a,b,p,K;
unordered_map<int,int>mp;
int main(){
scanf("%d%d%d",&p,&a,&b),K=sqrt(p);
int P=1;
for(int j=0;j<K;j++)mp[1ll*P*b%p]=j,P=1ll*P*a%p;
for(int i=1,j=P;i<=K;i++,j=1ll*j*P%p)if(mp.find(j)!=mp.end()){printf("%lld\n",1ll*i*K-mp[j]);return 0;}
puts("no solution");
return 0;
}
VI.II.[SDOI2013] 随机数生成器
我们来考虑一下:
日期 | 原式子 | 化简后式子 |
---|---|---|
于是我们要寻找 的解。
考虑上等比数列求和公式,得到
稍微整理一下
扔过去,得到
OK!是BSGS的形式了!直接上就行了……个锤子。
还要特判一堆东西。
- 。
这个东西虽然不很特殊,但是因为我们接下来众多特判都要判这个,所以就在开头统一判掉。答案:。
- 。
这时,除了第一天是 ,其余天都是 。因为我们已经特判了 的情形,所以只要特判 的情形,是则答案为 ,否则答案为 。
- 。
这时,无法使用等比数列求和,式子为 。整理得 。
但是,还要继续特判!如果 ,显然只有 时答案为 (这种情况已在开头特判过),否则答案为 。
否则,若 ,上式出来的结果是 ,要手动将其判作 ,因为 是质数,要 个 才能绕一圈。
- 。
此种情形下,无法除过去。除非 也为 ,否则答案即为 。但是这就意味着有 ,已经特判过了,所以不用再考虑这种情形,碰到直接判 即可。
- 。
常规BSGS一般不会考虑这种情形。但是,这就意味着 ,我们已经在最开头特判过了,故不用考虑。
代码:
#include<bits/stdc++.h>
using namespace std;
int T_T,p,a,b,X,T;
int ksm(int x,int y=p-2){
int z=1;
for(;y;y>>=1,x=1ll*x*x%p)if(y&1)z=1ll*z*x%p;
return z;
}
unordered_map<int,int>mp;
int main(){
scanf("%d",&T_T);
while(T_T--){
scanf("%d%d%d%d%d",&p,&a,&b,&X,&T);
if(X==T){puts("1");continue;}
if(!a){
if(T==b)puts("2");else puts("-1");
continue;
}
if(a==1){
T=(0ll+T-X+b+p)%p;
if(!b)puts("-1");else{
T=1ll*T*ksm(b)%p;
if(T)printf("%d\n",T);
else printf("%d\n",p);
}
continue;
}
b=1ll*b*ksm(a-1)%p;
X=(X+b)%p,T=(T+b)%p;
if(!X){puts("-1");continue;}
T=1ll*T*ksm(X)%p;
// printf("%d %d\n",a,T);
mp.clear();
int K=sqrt(p);
int P=1;
for(int j=0;j<K;j++)mp[1ll*P*T%p]=j,P=1ll*P*a%p;
bool fd=false;
for(int i=1,j=P;i<=K+1;i++,j=1ll*j*P%p)if(mp.find(j)!=mp.end()){printf("%d\n",i*K-mp[j]+1),fd=true;break;}
if(!fd)puts("-1");
}
return 0;
}
VI.III.CF1106F Lunar New Year and a Recursive Sequence
首先,我们不妨设 。则,因为递推式里面全都是一坨坨东西的幂再乘一起,初始的元素中又仅有 可能不为 ,所以明显每个元素都可以被表示成 的形式,其中 是我们接下来要求的东西。
我们发现,因为底数是相乘的,所以放在指数上就是相加,可以使用矩阵快速幂处理,只不过因为在指数上,所以要模 。
现在我们知道 了,则要求解的是 的形式。如何求解?
因为 有着地球人都知道的原根 ,所以我们不妨令 。于是原式转换为 ,继续转换得 。因为 已知,所以就是BSGS模板,随便套套就套出 来了。
虽然原题上说的是求”任意的 “,但是现行洛谷翻译上给的却是求”最小的 “,我也就照着翻译写了。因此我们这里介绍一下求最小 的做法。
首先, 一定是一个合法解;又因为 最短的循环节是 ,所以就有 。明显所有这样的 构成全部合法解的集合(但是会有重复的)。
这就得到了 。所有这样的 得到唯一的解集合。
唯一等价于 。于是我们设 ,就得到了解集为 。
现在,我们考虑找到上述集合中最小的数。显然,当 不算很大(比如 以内),可以枚举所有的 取 ;而当 较大时,集合中的数又不会很稀疏,可以从 开始一个一个枚举check它是否在集合内(当然,使用BSGS)。
时间复杂度比较玄学,取决于你取的分界点。反正我取了 作为分界点。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int phi=998244352;
int n,m,A;
struct Matrix{
int a[110][110];
Matrix(){memset(a,0,sizeof(a));}
int*operator[](const int&x){return a[x];}
friend Matrix operator*(Matrix&u,Matrix&v){
Matrix w;
for(int i=1;i<=A;i++)for(int j=1;j<=A;j++)for(int k=1;k<=A;k++)(w[i][j]+=1ll*u[i][k]*v[k][j]%phi)%=phi;
return w;
}
void print()const{for(int i=1;i<=A;i++){for(int j=1;j<=A;j++)printf("%d ",a[i][j]);puts("");}}
}M;
int ksm(int x,int y){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
int KSM(){
Matrix I;
for(int i=1;i<=A;i++)I[i][i]=1;
for(int y=n-A;y;y>>=1,M=M*M)if(y&1)I=I*M;
return I[A][A];
}
unordered_map<int,int>mp;
int BSGS(int a,int b){
if(b==0)return 1;
mp.clear();
int K=sqrt(mod);
int P=1;
for(int i=0;i<K;i++,P=1ll*P*a%mod)mp[1ll*P*b%mod]=i;
for(int i=1,j=P;i<=K+2;i++,j=1ll*j*P%mod)if(mp.find(j)!=mp.end())return i*K-mp[j];
return -1;
}
int main(){
scanf("%d",&A);
for(int i=1;i<=A;i++)scanf("%d",&M[A-i+1][A]);
for(int i=2;i<=A;i++)M[i][i-1]=1;
// M.print();
scanf("%d%d",&n,&m);
int a=KSM();
int c=BSGS(ksm(3,a),m);
if(c==-1){puts("-1");return 0;}
int K=__gcd(a,phi),R=phi/K;
if(K<=1048576){
int res=mod;
for(int i=0,j=ksm(3,c),k=ksm(3,R);i<K;i++,j=1ll*j*k%mod)res=min(res,j);
printf("%d\n",res);
}else for(int i=1;;i++)if(BSGS(3,i)%R==c%R){printf("%d\n",i);return 0;}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?