前言
人生中第一道紫题,写一篇题解纪念一下。
BSGS
BSGS(拔山盖世,北上广深 )是什么呢?
它是一种能够在 O ( n ) O(\sqrt n\space) O(n )的时间复杂度内求解类似于 a x ≡ b ( m o d p ) a^x \equiv b \pmod{p} ax≡b(modp)的算法。
首先,我们设 x = i p − j x=i\sqrt p-j x=ip−j,其中 i , j ∈ [ 0 , p ] i,j\in [0,\sqrt p\space] i,j∈[0,p ]
那么这个式子就变成了 a i p − j ≡ b ( m o d p ) a^{i\sqrt p-j} \equiv b\pmod p aip−j≡b(modp)
两边同时乘上 a j a^j aj,很显然,等式变成了
a i p ≡ b a j ( m o d p ) a^{i\sqrt p} \equiv ba^j\pmod p aip≡baj(modp)
到了这一步,我们就可以先枚举 j j j,每次把 b a j m o d p ba^j\bmod p bajmodp存入哈希表。
然后再枚举 i i i,在哈希表中查找 a i p a^{i\sqrt p} aip,如果找到了,就说明这个等式成立, x x x就应该是 i p − j i\sqrt p-j ip−j,如果枚举完了也没找着,就说说明这个等式不成立。
在这个过程中,我们仅仅枚举了两次,时间复杂度只有区区的 O ( n ) O(\sqrt n\space) O(n )
题目大意
看起来这个题目很复杂,但其实就是一个BSGS的模板。
简化一下题意,题目告诉你 g a m o d p g^a\bmod p gamodp和 g b m o d p g^b\bmod p gbmodp,让你求 g a b m o d p g^{ab}\bmod p gabmodp。
观察一下,我们发现
g
a
b
m
o
d
p
g^{ab}\bmod p
gabmodp
=
(
g
a
)
b
m
o
d
p
=(g^a)^b\bmod p
=(ga)bmodp
=
(
g
a
m
o
d
p
)
b
m
o
d
p
=(g^a\bmod p)^b\bmod p
=(gamodp)bmodp
=
A
b
m
o
d
p
= A^b\bmod p
=Abmodp
也就是说,只要我们把 b b b求出来,那就万事大吉了。
那么如何求 b b b呢?
观察发现:
B = g b m o d P B=g^b\bmod P B=gbmodP B ≡ g b ( m o d P ) B \equiv g^b\pmod P B≡gb(modP) g b ≡ B ( m o d P ) g^b \equiv B\pmod P gb≡B(modP)
这不就是BSGS的模板吗?
我们可以用BSGS求出 b b b,然后把用 b b b求出 A b m o d p A^b\bmod p Abmodp。
有了这个思路,我们就可以开始写代码了!
代码:
#include <bits/stdc++.h>
using namespace std;
int g,n,p;//在最开始就定义三个全局变量
int qpow(int a,int b){
//快速幂模板
int res=1;
while(b){
if(b&1) res=(a*res)%p;
a=(a*a)%p;
b>>=1;
}
return res;
}
int BSGS(int b){
map<int,int>m;//哈希表可以用map代替
int t=ceil(sqrt(p));//记得要向上取整
for(int i=1;i<=t;i++) m[b*qpow(g,i)%p]=i;//枚举j,存入哈希表中
for(int i=1;i<=t;i++){
int k=qpow(g,i*t)%p;
if(m[k]) return i*t-m[k];//如果在哈希表中找到了这个值,就说明找到了b
}
return -1;//如果没有找到,就说明无解
}
int main(){
cin>>g>>p>>n;
for(int i=1;i<=n;i++){
int A,B;
cin>>A>>B;
int b=BSGS(B);//用BSGS求出b
cout<<qpow(A,b)%p<<endl;//用快速幂求出K
}
return 0;
}
如果把这份代码提交到洛谷上,那么就会出现
万紫千红的场面。
我们可以尝试着开longlong。
#define int long long
提交后发现
WA没了,就变成了tle。
我们可以使用printf
和scanf
。
优化了却没有完全优化。
抱着侥幸的心理,我开了O2,结果发现。
这玄学的数据!
完整代码(记得开O2):
#include <bits/stdc++.h>
#define int long long
using namespace std;
int g,n,p;//在最开始就定义三个全局变量
int qpow(int a,int b){
//快速幂模板
int res=1;
while(b){
if(b&1) res=(a*res)%p;
a=(a*a)%p;
b>>=1;
}
return res;
}
int BSGS(int b){
map<int,int>m;//哈希表可以用map代替
int t=ceil(sqrt(p));//记得要向上取整
for(int i=1;i<=t;i++) m[b*qpow(g,i)%p]=i;//枚举j,存入哈希表中
for(int i=1;i<=t;i++){
int k=qpow(g,i*t)%p;
if(m[k]) return i*t-m[k];//如果在哈希表中找到了这个值,就说明找到了b
}
return -1;//如果没有找到,就说明无解
}
signed main(){
scanf("%lld%lld%lld",&g,&p,&n);
for(int i=1;i<=n;i++){
int A,B;
scanf("%lld%lld",&A,&B);
int b=BSGS(B);//用BSGS求出b
printf("%lld\n",qpow(A,b)%p);//用快速幂求出K
}
return 0;
}