专栏文章
大步小步/BSGS
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioof5ub
- 此快照首次捕获于
- 2025/12/02 22:31 3 个月前
- 此快照最后确认于
- 2025/12/02 22:31 3 个月前
BSGS 全称为 baby step giant step,用于求最小的 满足 。
主要思路基于分块与哈希。
首先我们知道若 ,那么满足 (费马小定理)。所以这意味着若有答案,则 一定小于 。
显然暴力枚举是不可行的,而 BSGS 算法基于分块使时间复杂度降低到了根号级别,可以通过。
具体而言,令 (防止未全部覆盖),则 可以表示为 。
化简式子:
因为 的范围都在 之间( 的范围是因为 ),所以我们考虑暴力预处理出每个 对应的 ,再把每个元素放到桶里,枚举每个 ,找到对应的 ,最终答案即为最先找到的 (由于 越小 越小)。时间复杂度为 。
具体实现,hash 我选择 unordered_map,速度接近 。
CPPint BSGS(int a,int b,int p) {
if(b == 1) return 0; //几个特判
if(a % p == b % p) return 1;
if(a % p == 0) return -1;
unordered_map<int,int> hash;
int siz = ceil(sqrt(p));
for(int i = 0,base = b % p;i <= siz;i++) hash[base] = i,base = base * a % p; //预处理任意j对应的结果
for(int i = 1,base = Pow(a,siz,p),tmp = 1;i <= siz;i++) {
tmp = tmp * base % p;
if(hash[tmp]) return (i * siz - hash[tmp]) % p; //容易发现i*siz-hash[tmp]>=0,所以无需+p再%p
}
return -1;
}
拼好题,第一问用快速幂,第二问求 ,第三问 BSGS 板子。
题意:求最小的 满足 。
观察式子,是一个递推的形状,:
容易发现,。
接下来是等比公式的相关应用,学过的可以跳过。
令 。
则:
上式减下式:
我们回到刚刚的式子:
把 放进来:
然后就转化为了 BSGS 的板子了。容易发现当 或 的不能很好用这个式子解决,所以特判处理。部分细节看代码。
CPP#include <bits/stdc++.h>
using namespace std;
template<class T>T Pow(T a,T b,T p){T ans=1;for(;b;b/=2){ans=((b&1?a:1)*ans%p),a=a*a%p;}return ans;}
template<class T>T Inv(T a,T b){return Pow(a,b-2,b);}
#define int long long
const int _ = 1e5 + 5;
int BSGS(int a,int b,int p) {
unordered_map<int,int> hash;
int siz = ceil(sqrt(p));
for(int i = 0,base = b % p;i <= siz;i++) hash[base] = i,base = base * a % p;
for(int i = 1,base = Pow(a,siz,p),tmp = 1;i <= siz;i++) {
tmp = tmp * base % p;
if(hash[tmp]) return ((i * siz - hash[tmp]) % p + p) % p;
}
return -1;
}
void solve() {
int p,a,b,x,t;
cin >> p >> a >> b >> x >> t;
if(x == t) { //相等直接输出1
cout << 1 << '\n';
} else if(a == 0) { //a=0时b=t
cout << (b == t ? 2 : -1) << '\n';
} else if(a == 1) { //a=1时注意答案是p的话就不要再取模了
t = ((t - x) % p + p) % p;
if(t % __gcd(b,p)) cout << -1 << '\n';
else {
if((t * Inv(b,p) + 1) % p == 0) cout << p << '\n';
else cout << (t * Inv(b,p) + 1) % p << '\n';
}
} else {
int inv = Inv(1 - a,p);
int ans = BSGS(a,((t - inv) % p + p) % p * Inv(((x - inv) % p + p) % p,p),p) % p;
if(ans == -1) cout << -1 << '\n';
else cout << ans + 1 << '\n';
}
}
signed main() {
int tt;
cin >> tt;
while(tt--) solve();
return 0;
}
这样的数不好转化,若我们能将其表示为某个数的次幂就好了。想到了 ,则 。
同余式子转化为:,化简得 ,成功转化为了 BSGS 的样子。
好像要用 __int128 或者龟速乘,我选择 __int128。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...