BSGS
Baby-Step Giant-Step,简称 BSGS。是一种在
处理对象
求解该同余方程的最小解,或者报告无解。保证
算法内容
前置内容
定义离散函数
::::info[如何求解
考虑如何求这个
| 记 |
|---|
如果上述同余方程存在解,那么一定属于
BSGS 算法
对同余方程进行变换。令
::::info[对于上式的一些思考]{open}
该内容对理解 BSGS 很有帮助,建议阅读。
考虑一种跳步的思想。
将
但有一个问题,我们怎么知道是否已经越过答案了。不好实现,于是考虑从答案下手进行处理。那么我们再对式子进行变换。
::::
由于
发现将原先拓展大步
枚举 map维护
左边我们预处理
实现
::::success[核心代码]
int T=ceil(sqrt(mod));//直接取模数作为上界
for(int i=0;i<=T;i++)
{
if(mp[mypow(A,T*i)]!=0)break;//出现相同说明进入新的函数周期 break 掉即可,某些时候可以优化常数。
mp[mypow(A,T*i)]=T*i;
}
for(int i=0;i<=T;i++)
{
int cnt=(B*mypow(A,i))%mod;
if(mp[cnt])ans=min(ans,mp[cnt]-i);
}
::::
例题
P3846 [TJOI2007] 可爱的质数
这是一道 BSGS 的裸题直接实现上述算法即可。
在实现 BSGS 的时候,求出 break掉即可。
::::success[Accepted code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int V=1LL<<31;
int A,B,mod;
unordered_map<int,int>mp;
int mypow(int a,int k)
{
if(k==0)return 1;
int res=a;k--;
while(k)
{
if(k&1)res=(res*a)%mod;
a=(a*a)%mod;
k>>=1;
}
return res;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>mod>>A>>B;
int T=ceil(sqrt(mod));//直接取模数作为上界
for(int i=0;i<=T;i++)
{
if(mp[mypow(A,T*i)]!=0)break;//出现相同说明进入新的函数周期 break 掉即可,某些时候可以优化常数。
mp[mypow(A,T*i)]=T*i;
}
int ans=1e18;
for(int i=0;i<=T;i++)
{
int cnt=(B*mypow(A,i))%mod;
if(mp[cnt])ans=min(ans,mp[cnt]-i);
}
if(ans==1e18)cout<<"no solution";
else cout<<ans;
return 0;
}
/*
3 2 2
*/
::::
P2485 [SDOI2011] 计算器
还是模板题,操作
注意操作
::::success[Accepted code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
int mod;
int mypow(int a,int k)
{
if(k==0)return 1;
int res=a;k--;
while(k)
{
if(k&1)res=(res*a)%mod;
a=(a*a)%mod;
k>>=1;
}
return res;
}
unordered_map<int,int>mp;
int BSGS(int A,int B)
{
mp.clear();
if(B==1)
{
return 0;
}
int T=ceil(sqrt(mod));
for(int i=1;i<=T;i++)
{
if(mp[mypow(A,T*i)]!=0)mp[mypow(A,T*i)]=min(mp[mypow(A,T*i)],T*i);
else mp[mypow(A,T*i)]=T*i;
}
int ans=1e18;
for(int i=0;i<=T;i++)
{
int cnt=(B*mypow(A,i))%mod;
if(mp[cnt])ans=min(ans,mp[cnt]-i);
}
if(ans==1e18)return -1;
return ans;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T,K;cin>>T>>K;
while(T--)
{
int y,z;cin>>y>>z>>mod;
if(K==1)
{
cout<<mypow(y,z)<<"\n";
continue;
}
if(K==2)
{
if(z%__gcd(y,mod))cout<<"Orz, I cannot find x!\n";
else cout<<(z*mypow(y,mod-2))%mod<<"\n";
continue;
}
if(K==3)
{
if(y%mod==1&&z%mod==1)cout<<"0\n";
else if(y%mod==z%mod)cout<<"1\n";
else if(y%mod==0)
{
if(z%mod==1)cout<<"0\n";
else cout<<"Orz, I cannot find x!\n";
}
else
{
int res=BSGS(y,z);
if(res==-1)cout<<"Orz, I cannot find x!\n";
else cout<<res<<"\n";
}
}
}
return 0;
}
::::
P3306 [SDOI2013] 随机数生成器
结合了一定拆式子和特判的 BSGS 题。
给出
x_1,a,b,p,t 和x_i 的递推式x_{i+1}\equiv a\times x_i+b\pmod p 。求最小l 使x_l=t 。
考虑拆出通项公式。
::::info[拆式子]
手动推一下前
容易发现。
等比数列求和得到通项公式,注意
::::
拆完式子得到。
直接代
题目
::::warning[特殊情况]{open} 接下来就是几个特判要处理。
第一天比较特殊,直接特判是否
由于使用的等比数列,
对于
根据裴蜀定理,该方程存在解。当且仅当
if(a==1)
{
t=(t-x+mod)%mod;
if(t%__gcd(b,mod))cout<<"-1\n";
else
{
if((t*mypow(b,mod-2)+1)%mod==0)cout<<mod<<"\n";//不可能是第 0 天,最先的一天为第 p 天
else cout<<(t*mypow(b,mod-2)+1)%mod<<"\n";
}
}
:::: ::::success[Accepted code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
int mod,a,b,x,t;
int mypow(int a,int k)
{
if(k==0)return 1;
int res=a;k--;
while(k)
{
if(k&1)res=(res*a)%mod;
a=(a*a)%mod;
k>>=1;
}
return res;
}
unordered_map<int,int>mp;
int BSGS(int A,int B)
{
mp.clear();
int T=ceil(sqrt(mod));
for(int i=0;i<=T;i++)
{
if(mp[mypow(A,T*i)]!=0)break;
else mp[mypow(A,T*i)]=T*i;
}
int ans=1e18;
for(int i=0;i<=T;i++)
{
int cnt=(B*mypow(A,i))%mod;
if(mp[cnt])ans=min(ans,mp[cnt]-i);
}
if(ans==1e18)return -1;
return ans;
}
void solve()
{
cin>>mod>>a>>b>>x>>t;
if(x==t)
{
cout<<1<<"\n";
return ;
}
if(a==0)
{
if(b%mod!=t)cout<<"-1\n";
else cout<<"2\n";
return ;
}
if(a==1)
{
t=(t-x+mod)%mod;
if(t%__gcd(b,mod))
{
cout<<"-1\n";
}
else
{
if((t*mypow(b,mod-2)+1)%mod==0)cout<<mod<<"\n";
else cout<<(t*mypow(b,mod-2)+1)%mod<<"\n";
}
return ;
}
int B=(((a*t-t+b)%mod)*mypow((x*a-x+b)%mod,mod-2))%mod;
int res=BSGS(a,B);if(res!=-1)res++;//计算出来的是 l-1
cout<<res<<"\n";
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;cin>>T;
while(T--)solve();
return 0;
}
::::