BSGS
BSGS,中文名大步小步算法,用于在 O ( p ) O(\sqrt p) O(p) 时间内求解这样一个同余式:
a x ≡ b ( m o d p ) a^x\equiv b(\mod p) ax≡b(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×t−j(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×t−j≡b(modp)⇒(at)i≡b×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×t−j,若顺序枚举 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×ax−1≡gb(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×ax−cnt≡b(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) ax−cnt≡b×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
不过理解万岁啦。