BSGS算法简述
对于一个方程:
求能满足条件的最小的 x(GCD(A,C)=1) x ( G C D ( A , C ) = 1 )
由于 GCD(A,C)=1 G C D ( A , C ) = 1 ,所以一定满足 Aφ(C)≡1(mod C) A φ ( C ) ≡ 1 ( m o d C ) 则必然有解。
在此基础上,很容易发现最优解一定在 [0,C)中 [ 0 , C ) 中
利用分块的思想,取
m=C−−√
m
=
C
设
i=x/m,j=x%m
i
=
x
/
m
,
j
=
x
%
m
那么原式即可表示为:
Ai∗m+j≡B(mod C)
A
i
∗
m
+
j
≡
B
(
m
o
d
C
)
两侧同乘
A−i∗m
A
−
i
∗
m
,可得
B∗(A−m)i≡Aj(mod C)
B
∗
(
A
−
m
)
i
≡
A
j
(
m
o
d
C
)
于是,我们就可以通过枚举
(A−m)i∗B
(
A
−
m
)
i
∗
B
,同时查找是否其值刚好为
Aj(j∈[0,m))
A
j
(
j
∈
[
0
,
m
)
)
可以将
Aj(j∈[0,m))用O(m)
A
j
(
j
∈
[
0
,
m
)
)
用
O
(
m
)
预处理,并存入hash表中(map)
这样一来,整个算法的复杂度就降到了
O(N−−√)
O
(
N
)
拓展BSGS
在普通的BSGS中,存在一个极为严苛的前提
(GCD(A,C)=1)
(
G
C
D
(
A
,
C
)
=
1
)
若不满足这个条件,是否仍然能求出最小的x呢?
这就是拓展BSGS的用处了。
现在再回来看这个式子:
首先,对于任意一个数 d d ,若则必然满足 d|B d | B (除非 B=1 B = 1 )
这一点应该很显然,因为我们可以将取模用乘法来表示,这样由于A,C均为d的倍数,将其都移至左侧,则右侧只有B,其也必须是d的倍数才能满足。
假设现在我们从A,B,C中都除以d,原式则变为:
由于 Ad A d 是一个常数,所以我们可以将其全部设为 D D
即原式为
这样一来,我们相当于把原问题的C转化为了 Cd=C′ C d = C ′ ,B转化为了 Bd=B′ B d = B ′ 。
持续以上操作,直到 GCD(A,C′)=1 G C D ( A , C ′ ) = 1 ,若中途任意时刻, B B <script type="math/tex" id="MathJax-Element-28">B</script>不为d的倍数,则停止,判断为无解。
最后,只要套回原问题解决即可,只是左侧多了一个D(常数)
模板题(普通BSGS)POJ2417:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65536
#define MAXP 65537
using namespace std;
long long fsp(long long x,long long y,long long mod){
long long res=1;
while(y){
if(y&1ll)
res=(res*x)%mod;
y>>=1ll;
x=(x*x)%mod;
}
return res;
}
vector<int> num[MAXP+100],tr[MAXP+100];
void Insert(long long x,int val){
long long x1=x%MAXP;
for(int i=0;i<num[x1].size();i++)
if(tr[x1][i]==x)
return ;
num[x1].push_back(val);
tr[x1].push_back(x);
}
int read(long long x){
long long x1=x%MAXP;
for(int i=0;i<num[x1].size();i++)
if(tr[x1][i]==x)
return num[x1][i];
return -1;
}
int solve(long long n,long long a,long long b){
long long m=1ll*ceil(sqrt(n+0.5));
long long v=fsp(fsp(a,m,n),n-2,n);
long long e=1;
for(int i=0;i<m;i++){
Insert(e,i);
e=(e*a)%n;
}
e=b;
for(int i=0;i<m;i++){
int res1=read(e);
if(res1>=0)
return i*m+res1;
e=(e*v)%n;
}
return -1;
}
int main(){
long long p,n,b;
while(SF("%lld%lld%lld",&p,&b,&n)!=EOF){
memset(num,0,sizeof num);
memset(tr,0,sizeof tr);
int res=solve(p,b,n);
if(res>=0)
PF("%d\n",res);
else
PF("no solution\n");
}
}
模板题(拓展BSGS)POJ3243:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65536
#define MAXP 65537
using namespace std;
int numc;
long long fsp(long long x,long long y,long long mod){
long long res=1;
while(y){
if(y&1ll)
res=(res*x)%mod;
y>>=1ll;
x=(x*x)%mod;
}
return res;
}
vector<int> num[MAXP+100],tr[MAXP+100];
void Insert(long long x,int val){
long long x1=x%MAXP;
for(int i=0;i<num[x1].size();i++)
if(tr[x1][i]==x)
return ;
num[x1].push_back(val);
tr[x1].push_back(x);
}
int read(long long x){
long long x1=x%MAXP;
for(int i=0;i<num[x1].size();i++)
if(tr[x1][i]==x)
return num[x1][i];
return -1;
}
long long exgcd(long long a,long long b,long long &x,long long &y){
if(b==0){
x=1;
y=0;
return a;
}
long long g=exgcd(b,a%b,x,y);
long long xx=x,yy=y;
y=xx-a/b*yy;
x=yy;
return g;
}
int solve(long long a,long long b,long long n,long long d){
long long m=1ll*ceil(sqrt(n+0.5));
long long e=d%n;
for(int i=0;i<=m;i++){
Insert(e,i);
e=(e*a)%n;
}
long long xx,yy;
long long v=fsp(a,m,n);
long long g=exgcd(v,n,xx,yy);
v=xx/g;
v=(v%(n/g)+(n/g))%(n/g);
if(v==0)
v=(n/g);
e=b;
for(int i=0;i<m;i++){
int res1=read(e);
if(res1>=0)
return i*m+res1;
e=(e*v)%n;
}
return -1;
}
long long prepare(long long a,long long &b,long long &n){
long long res=1,xx,yy,a1=1%n;
long long g=exgcd(a,n,xx,yy);
for(int i=0;i<=100;i++){
if(a1==b){
PF("%d\n",i);
return 0;
}
a1=(a1*a)%n;
}
while(g!=1){
n=n/g;
res=(res*(a/g))%n;
numc++;
if(b%g)
return -1;
b/=g;
g=exgcd(a,n,xx,yy);
}
return res;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
long long a,n,b;
while(SF("%lld%lld%lld",&a,&n,&b)!=EOF){
if(!a&&!b&&!n)
break;
a%=n;
b%=n;
memset(num,0,sizeof num);
memset(tr,0,sizeof tr);
numc=0;
long long d=prepare(a,b,n);
if(d==0)
continue;
if(d<0){
PF("No Solution\n");
continue;
}
int ans=solve(a,b,n,d);
if(ans>=0)
PF("%d\n",ans+numc);
else
PF("No Solution\n");
}
}