欧拉定理 a ϕ ( p ) ≡ 1 ( m o d p ) a^{\phi(p)}\equiv1\pmod{p} aϕ(p)≡1(modp)
扩展欧拉定理
g
r
≡
g
[
r
m
o
d
  
ϕ
(
p
)
]
+
ϕ
(
p
)
(
m
o
d
p
)
g^r\equiv g^{[r\mod{\phi(p})]+\phi(p)}\pmod{p}
gr≡g[rmodϕ(p)]+ϕ(p)(modp)
特殊地、当
(
r
,
p
)
=
1
(r,p)=1
(r,p)=1 时
g
r
≡
g
r
m
o
d
  
ϕ
(
p
)
(
m
o
d
p
)
{g^r\equiv g^{r\mod{\phi(p)}}\pmod{p}}
gr≡grmodϕ(p)(modp)
BSGS:求 g r ≡ a ( m o d p ) g^r\equiv a\pmod{p} gr≡a(modp) 的最小正整数解 r x r_x rx 。
由扩展欧拉定理
0
<
r
x
<
p
0<r_{x}<p
0<rx<p 。
设
r
=
i
m
−
j
r=im-j
r=im−j 得到
g
i
m
≡
a
g
j
(
m
o
d
p
)
g^{im}\equiv ag^{j}\pmod{p}
gim≡agj(modp) 并且有
0
<
i
m
−
j
<
p
0<im-j<p
0<im−j<p 。
j
∈
[
0
,
m
)
j\in[0,m)
j∈[0,m) ,对应的
i
i
i 可能的取值为
[
1
,
⌈
p
m
⌉
]
[1,\lceil\frac{p}{m}\rceil]
[1,⌈mp⌉] 。
所以枚举
j
∈
[
0
,
m
)
j\in[0,m)
j∈[0,m) ,存下
a
g
j
(
m
o
d
p
)
ag^j\pmod{p}
agj(modp) 对应最大的
j
j
j 。
然后枚举
i
∈
[
1
,
m
]
i\in[1,m]
i∈[1,m] ,计算
g
i
m
(
m
o
d
p
)
g^{im}\pmod{p}
gim(modp) 查表得到
i
m
−
j
im-j
im−j 。
复杂度
O
(
max
(
m
,
⌈
p
m
⌉
)
)
O(\max(m,\lceil\frac{p}{m}\rceil))
O(max(m,⌈mp⌉)) 所以我们取
m
=
p
m=\sqrt{p}
m=p 。
查表用 hash 会快,用 map 虽然牺牲一个 log 但是(也许)稳一点。
或者用 stl::hash_map?
打完 hash 我的感觉就是 尽量用 map 吧 (
不过写写 hash 也不是很难(?) 模数也比较宽松
注意题目的条件,同时也是不加特判 BSGS 的限制。就是那堆
2
≤
⋯
2\le\cdots
2≤⋯
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define PIL pair<int, long long>
const int HASHSIZE = 1 << 18;
const long long HASHMOD = 1ll * (HASHSIZE - 1);
long long p, g, a, m;
struct Node
{
int Index;
long long Value;
int Next;
Node(int a = -1, long long b = -1, int c = -1)
{
Index = a;
Value = b;
Next = c;
}
bool IsEmpty()
{
return (Index==-1)&&(Value==-1)&&(Next==-1);
}
} HashList[HASHSIZE+5];
int GetHashValue(long long OriginalValue)
{
return (int) (OriginalValue & HASHMOD);
}
long long QuickPow(long long Base, long long Index, long long Mod)
{
long long Result = 1;
Base %= Mod;
while (Index)
{
if (Index & 1)
{
Result *= Base;
Result %= Mod;
}
Base *= Base;
Base %= Mod;
Index >>= 1;
}
return Result;
}
void HashInsert(long long Value, long long Index)
{
int HashValue = GetHashValue(Value);
int HashPos = HashValue;
while (HashList[HashPos].Next != -1) HashPos = HashList[HashPos].Next;
if (!HashList[HashPos].IsEmpty())
{
while(!HashList[HashList[HashPos].Next].IsEmpty()) ++HashList[HashPos].Next;
HashPos = HashList[HashPos].Next;
}
HashList[HashPos] = Node(Index, Value);
}
long long HashQuery(long long Value)
{
int HashPos = GetHashValue(Value);
while (HashList[HashPos].Next != -1 && HashList[HashPos].Value != Value) HashPos = HashList[HashPos].Next;
if (HashList[HashPos].IsEmpty() || HashList[HashPos].Value!=Value) return -1;
return HashList[HashPos].Index;
}
int main()
{
scanf("%lld%lld%lld", &p, &g, &a);
m = (long long)ceil(sqrt(1.0*p));
for (long long j = 0, t = a; j < m; ++j, t *= g, t %= p) HashInsert(t, j);
for (long long tem, i = 1, t = QuickPow(g, m, p), s = t; i <= m; ++i, s *= t, s %= p)
{
tem = HashQuery(s);
if (tem != -1)
{
printf("%lld", i * m - tem);
return 0;
}
}
printf("no solution");
return 0;
}