BSGS exBSGS学习笔记

BSGS

BSGS,中文名大步小步算法,用于在 O ( p ) O(\sqrt p) O(p ) 时间内求解这样一个同余式:

a x ≡ b ( m o d    p ) a^x\equiv b(\mod p) axb(modp),其中 a a a b b b p p p 已知且 p p p 为质数。

算法思想:设 t = ⌈ p ⌉ t=\left\lceil\sqrt p\right\rceil t=p ,由于若有解,则最小非负整数解 x x x 一定在 [ 0 , p ] [0,p] [0,p] 内,所以 x x x 一定可以被表示为形如 i × t − j ( i ∈ [ 1 , t ] , j ∈ [ 0 , t ] ) i\times t-j(i\in[1,t],j\in[0,t]) i×tj(i[1,t],j[0,t]) 的形式,由此可以将同余式变形:

a i × t − j ≡ b ( m o d    p ) ⇒ ( a t ) i ≡ b × a j ( m o d    p ) a^{i\times t-j}\equiv b(\mod p)\Rightarrow (a^t)^i\equiv b\times a^j(\mod p) ai×tjb(modp)(at)ib×aj(modp)

于是我们可以先枚举 j j j,将 b × a j b\times a^j b×aj 的值插入哈希表或map中与 j j j 对应,再枚举 i i i 进行查询,若在哈希表或map中找到了 ( a t ) i (a^t)^i (at)i 的值,则其中一个答案为 i × t − j i\times t-j i×tj,若顺序枚举 i i i,则第一个找到的合法解就是最小解。

代码里判了三种无解情况,并用unordered_map优化了一下:

inline int bsgs(int a,int b,int p){
	b%=p;
	if(a%p==0&&b) return -1;
	int t=ceil(sqrt(p)),x=b;
	ff(j,0,t){
		mp[x]=j;
		x=1ll*x*a%p;
	}
	int base=ksm(a,t,p),now=1;
	if(!base) return b==0?1:-1;
	ff(i,1,t){
		now=1ll*now*base%p;
		if(mp[now]) return i*t-mp[now];
	}
	return -1;
}

(洛谷的数据真的水水水啊

exBSGS

a a a p p p 不互质的时候,想利用BSGS解方程,就要考虑怎么把 a a a p p p 变为互质:

g = gcd ⁡ ( a , p ) g=\gcd (a,p) g=gcd(a,p),则原式可以转化成 a g × a x − 1 ≡ b g ( m o d    p g ) \frac{a}{g}\times a^{x-1}\equiv \frac{b}{g}(\mod\frac{p}{g}) ga×ax1gb(modgp),那么根据学数学摸爬滚打出的经验,当 b m o d    g ≠ 0 b\mod g\neq 0 bmodg=0 b ≠ 1 b\neq 1 b=1时无解( b = 1 b=1 b=1 的时候只要 x = 0 x=0 x=0 就可以了),这样一直将 b b b p p p 除下去,最终 a a a p p p 一定互质。

设每次操作得到的的 a g \frac{a}{g} ga 累乘起来为 k k k,最终一共操作了 c n t cnt cnt 次,则当前方程变为 k × a x − c n t ≡ b ( m o d    p ) k\times a^{x-cnt}\equiv b(\mod p) k×axcntb(modp),由于互质,逆元一定存在,可以用扩欧求出 k k k 的逆元 i n v inv inv,方程又变为 a x − c n t ≡ b × i n v ( m o d    p ) a^{x-cnt}\equiv b\times inv(\mod p) axcntb×inv(modp),可以BSGS求解。

inline void exbsgs(int a,int b,int p){
	int g=__gcd(a,p),k=1,cnt=0;
	if(p==1||b==1) return printf("0\n"),void();
	while(g!=1){
		if(b==k) return printf("%lld\n",cnt),void();
		if(b%g) return printf("No Solution\n"),void();
		++cnt,b/=g,p/=g,k=k*a/g%p;
		g=__gcd(a,p);
	}
	int inv,y;
	exgcd(k,p,inv,y);
	inv=(inv%p+p)%p;
	b=b*inv%p;
	int res=bsgs(a,b,p);
	if(res==-1) printf("No Solution\n");
	else printf("%lld\n",res+cnt);
}

这个代码加上一个BSGS在洛谷过不去,一直TLE#11,数据加强成这样就很真的绝了/fn

不过理解万岁啦。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值