bsgs算法
Baby Step Giant Step算法,简称BSGS算法,也称为大步小步算法.
解决对象
离散对数:当
x≡Gk(modm)
x
≡
G
k
(
mod
m
)
时,
logG(x)≡k(modϕ(m))
l
o
g
G
(
x
)
≡
k
(
mod
ϕ
(
m
)
)
。此处的
logG(x)
l
o
g
G
(
x
)
是
x
x
以整数为底,模
ϕ(m)
ϕ
(
m
)
的离散对数。
BSGS算法就是用来求解
Ax≡B(modC)
A
x
≡
B
(
mod
C
)
。原始的BSGS算法只能解决
C
C
是质数的问题。
求解步骤
设向下取整,
x=i×m+j
x
=
i
×
m
+
j
,那么,
Ax=(Am)i×Aj,0≤i<m,0≤j<m
A
x
=
(
A
m
)
i
×
A
j
,
0
≤
i
<
m
,
0
≤
j
<
m
。然后可以枚举
i
i
,这是一个级别的枚举。
对于枚举的每一个
i
i
,都令,令
E=Aj
E
=
A
j
,那么就有
D×E≡B(modC)
D
×
E
≡
B
(
mod
C
)
。然后用扩展欧几里得求解。
不过还应该特判
A
A
是的倍数的情况,比较容易。
对于多次调用
Aj
A
j
的情况,不用每次都用快速幂,只需要扔进hash表就可以了。
基本代码(用map模拟hash)
这道题
采用map,代码量比hash少的多了!!
(hash我调了好几天没调出来,后来换成map就过了,肯定是hash写错了)
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
int A,B,C;
namespace ksm
{
long long qpow(long long a,long long t,long long p)
{
int base=a%p;
long long ans=1;
while(t)
{
if(t&1)
ans=(long long)ans%p*(base%p)%p;
base=(long long)base%p*(base%p)%p;
t>>=1;
}
return ans;
}
}
namespace BSGS
{
map<long long,int>mp;
int bsgs(int A,int B,int C)
{
mp.clear();
if(A%C==0)
return -1;
int p=false;
int m=ceil(sqrt(C));
long long ans;
for(int i=0;i<=m;i++)
{
if(!i)
{
ans=B%C;
mp[ans]=i;
continue;
}
ans=ans*A%C;
mp[ans]=i;
}
long long t=ksm::qpow(A,m,C);ans=1;
for(int i=1;i<=m;i++)
{
ans=(ans*t)%C;
if(mp[ans])
{
int t=i*m-mp[ans];
return (t%C+C)%C;
p=true;
break;
}
}
if(!p)return -1;
}
}
int main()
{
int t=read();
int l=read();
if(l==1)
{
using namespace ksm;
while(t--)
{
int a=read();
int c=read();
int b=read();
printf("%lld\n",qpow(a,c,b));
}
return 0;
}
if(l==2)
{
using namespace ksm;
while(t--)
{
int y=read();
int z=read();
int p=read();
int gcd=__gcd(y,p);
if(z%gcd)
{
printf("Orz, I cannot find x!\n");
continue;
}
printf("%d\n",((z%p)*(long long)qpow(y,p-2,p))%p);
}
}
if(l==3)
{
using namespace BSGS;
using namespace ksm;
while(t--)
{
A=read();B=read();C=read();
long long ans=bsgs(A,B,C);
if(ans==-1)
{
printf("Orz, I cannot find x!\n");
continue;
}
printf("%lld\n",ans);
}
}
return 0;
}
鸣谢这位大佬map的启示!
我是传送门
SDOI2011 计算器
题目描述
你被要求设计一个计算器完成以下三项任务:
1、给定y、z、p,计算
yzmodp
y
z
m
o
d
p
的值;
2、给定y、z、p,计算满足
xy≡z
x
y
≡
z
(mod p)的最小非负整数x;
3、给定y、z、p,计算满足
yx≡z(mod p)
y
x
≡
z
(
m
o
d
p
)
的最小非负整数x。
为了拿到奖品,全力以赴吧!
输入输出格式
输入格式:
输入文件calc.in 包含多组数据。
第一行包含两个正整数T、L,分别表示数据组数和询问类型(对于一个测试点内的所有数
据,询问类型相同)。
以下T 行每行包含三个正整数y、z、p,描述一个询问。
输出格式:
输出文件calc.out 包括T 行.
对于每个询问,输出一行答案。
对于询问类型2 和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”。
输入输出样例
输入样例#1:
3 1
2 1 3
2 2 3
2 3 3
输出样例#1:
2
1
2
输入样例#2:
3 2
2 1 3
2 2 3
2 3 3
输出样例#2:
2
1
0
输入样例#3:
4 3
2 1 3
2 2 3
2 3 3
2 4 3
输出样例#3:
0
1
Orz, I cannot find x!
0
数据规模和约定
题解
这里有三种问法,第一种问法就直接是带模的快速幂,没啥好说的。
第二种问法就是求线性方程解,那就用扩展欧几里得!
第三种问法就是本文考虑的BSGS算法,也就是一个板子题目吧。怎么写板子前面已经介绍过了。
code
就是纯板子吧,直接照搬上面的代码就行了。
扩展BSGS
传统的bsgs只能解决c是质数的问题,如果c是合数,那就需要使用扩展bsgs。因为不是质数,所以不能直接求逆元。
扩展BSGS大概有那么几几个步骤:
设
d=gcd(A,C)
d
=
g
c
d
(
A
,
C
)
,那么
A,B,C
A
,
B
,
C
可以表示为
A=a∗d,B=b∗d
A
=
a
∗
d
,
B
=
b
∗
d
(如果B不是d的倍数且
B!=1
B
!
=
1
则显然无解),
C=c∗d
C
=
c
∗
d
。代入原式可得
a×(a×d)x−1≡b(modc)
a
×
(
a
×
d
)
x
−
1
≡
b
(
mod
c
)
,如果我们把
D×a
D
×
a
,那么左边就只剩下
(a×d)x−1了
(
a
×
d
)
x
−
1
了
,不停地求
d=gcd(A,C)
d
=
g
c
d
(
A
,
C
)
,然后将B和C分别除以d。D要乘A/d,num++,直到d=1为止。
最后方程就成为这种形式了:
D×Ax−num≡B(modC)
D
×
A
x
−
n
u
m
≡
B
(
mod
C
)
。那么令
x=i×m+j+num
x
=
i
×
m
+
j
+
n
u
m
,那么原先的BSGS一样处理就可以了。但是需要注意的是,x