BSGS算法及扩展

BSGS算法

BabyStepGiantStep算法,即大步小步算法,缩写为BSGS
拔山盖世算法

它是用来解决这样一类问题
yx=z(mod p),给定y,z,p>=1求解x

普通的BSGS只能用来解决gcd(y,p)=1的情况

x=am+b,m=p,a[0,m),b[0,m)
那么yam=zyb(mod p)

怎么求解,为了方便,设x=amb
那么yam=zyb(mod p),a(0,m+1]

直接暴力辣,把右边的b枚举[0,m),算出zyb(mod p),哈希存起来
然后左边a枚举(0,m+1],算出yam(mod p)查表就行了

然后不知道为什么要用exgcd,只会map...

代码

[SDOI2011]计算器

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

template <class Int>
IL void Input(RG Int &x){
    RG int z = 1; RG char c = getchar(); x = 0;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    x *= z;
}

IL void None(){
	puts("Orz, I cannot find x!");
}

int p;

IL int Pow(RG ll x, RG ll y){
	RG ll ret = 1;
	for(; y; x = x * x % p, y >>= 1)
		if(y & 1) ret = ret * x % p;
	return ret;
}

map <int, int> pw;

IL void BSGS(RG int x, RG int y){
	pw.clear();
	if(y == 1){
		puts("0");
		return;
	}
	RG int ans = -1, m = sqrt(p) + 1, xx, s = y;
	for(RG int i = 0; i < m; ++i){
		pw[s] = i;
		s = 1LL * s * x % p;
	}
	xx = Pow(x, m), s = 1;
	for(RG int i = 1; i <= m + 1; ++i){
		s = 1LL * s * xx % p;
		if(pw.count(s)){
			ans = i * m - pw[s];
			break;
		}
	}
	if(ans < 0) None();
	else printf("%d\n", ans);
}

int T, k, y, z;

int main(RG int argc, RG char* argv[]){
	for(Input(T), Input(k); T; --T){
		Input(y), Input(z), Input(p);
		if(k == 1) printf("%d\n", Pow(y, z));
		else if(k == 2){
			RG int d = (y % p) ? 1 : p;
			if(z % d) None();
			else printf("%lld\n", 1LL * Pow(y, p - 2) * z % p);
		}
		else{
			if(y % p) BSGS(y % p, z % p);
			else None();
		}
	}
    return 0;
}

扩展BSGS

对于gcd(y,p)1怎么办?

我们把它写成yyx1+kp=z,kZ的形式

根据exgcd的理论
那么如果y,pgcd不是z的约数就不会有解

d=gcd(y,p)
那么

ydyx1+kpd=zd

递归到d=1
设之间的所有的d的乘积为g,递归c
x=xc,p=pg,z=zg
那么

yxycg=z(mod p)

那么BSGS求解就好了

代码

SPOJMOD Power Modulo Inverted

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
using namespace std;
typedef long long ll;

template <class Int>
IL void Input(RG Int &x){
    RG int z = 1; RG char c = getchar(); x = 0;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    x *= z;
}

map <int, int> pw;

IL int Gcd(RG int x, RG int y){
	return !y ? x : Gcd(y, x % y);
}

IL int Pow(RG ll x, RG ll y, RG int p){
	RG ll ret = 1;
	for(; y; x = x * x % p, y >>= 1)
		if(y & 1) ret = ret * x % p;
	return ret;
}

int a, b, p;

IL int EX_BSGS(){
	if(b == 1) return 0;
	pw.clear();
	RG int cnt = 0, t = 1, s, x, m;
	for(RG int d = Gcd(a, p); d != 1; d = Gcd(a, p)){
		if(b % d) return -1;
		++cnt, b /= d, p /= d, t = 1LL * t * a / d % p;
		if(b == t) return cnt;
	}
	s = b, m = sqrt(p) + 1;
	for(RG int i = 0; i < m; ++i){
		pw[s] = i;
		s = 1LL * s * a % p;
	}
	x = Pow(a, m, p), s = t;
	for(RG int i = 1; i <= m; ++i){
		s = 1LL * s * x % p;
		if(pw.count(s)) return i * m - pw[s] + cnt;
	}
	return -1;
}

int ans;

int main(RG int argc, RG char* argv[]){
	for(Input(a), Input(p), Input(b); a + b + p;){
		a %= p, b %= p, ans = EX_BSGS();
		if(ans < 0) puts("No Solution");
		else printf("%d\n", ans);
		Input(a), Input(p), Input(b);
	}
    return 0;
}

posted @   Cyhlnj  阅读(4006)  评论(4编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示