题目链接
解题思路
P已知,是一个素数,
1
e
9
≤
P
≤
1
e
14
1e9 \le P \le1e14
1e9≤P≤1e14。Q未知,Q满足以下条件:Q是小于P的最大素数。
所以问题分两步解决:先确定Q,然后求Q的阶乘。
如何确定Q呢?只需要判断素数就行了,从P开始,依次递减,直到找到某个数是素数,这个数就是Q了。
接下来就是求
Q
!
Q!
Q! 了,因为Q会很大,所以不能暴力求阶乘,这里利用威尔逊定理:当且仅当P为素数时:( P -1 )! ≡ -1 ( mod P )
(
P
−
1
)
!
≡
−
1
(
m
o
d
P
)
≡
(
P
−
1
)
(
m
o
d
P
)
(P-1)! \equiv -1(mod P) \equiv (P-1)(mod P)
(P−1)!≡−1(modP)≡(P−1)(modP)
所以有:
Q
!
≡
1
(
Q
+
1
)
×
(
Q
+
2
)
.
.
.
(
P
−
3
)
×
(
P
−
2
)
(
m
o
d
P
)
Q! \equiv {1 \over(Q+1) \times(Q+2)...(P-3)\times(P-2)}(mod P)
Q!≡(Q+1)×(Q+2)...(P−3)×(P−2)1(modP)
≡
1
(
Q
+
1
)
×
1
(
Q
+
2
)
.
.
.
1
(
P
−
3
)
×
1
(
P
−
2
)
(
m
o
d
P
)
\equiv {1 \over(Q+1) } \times{1 \over(Q+2) }... {1 \over(P-3) } \times{1 \over(P-2) }(mod P)
≡(Q+1)1×(Q+2)1...(P−3)1×(P−2)1(modP)
接下来结合费马小定理求逆元:
Q
!
≡
(
Q
+
1
)
P
−
2
×
(
Q
+
2
)
P
−
2
.
.
.
(
P
−
3
)
P
−
2
×
(
P
−
2
)
P
−
2
(
m
o
d
P
)
Q! \equiv (Q+1)^{P-2} \times (Q+2)^{P-2} ... (P-3)^{P-2} \times (P-2)^{P-2}(mod P)
Q!≡(Q+1)P−2×(Q+2)P−2...(P−3)P−2×(P−2)P−2(modP)
然后快速幂求就行了,需要注意的是,一次
(
Q
+
1
)
×
(
Q
+
1
)
(Q+1) \times (Q + 1)
(Q+1)×(Q+1) 就会爆long long,所以可以使用__int128或者快速乘来计算。
源码如下:
#include<cstdlib>
#include<ctime>
#include<cstdio>
#include <iostream>
#define ll long long
using namespace std;
const int count=20;
ll modular_exp(ll a,ll m,ll n)
{
if(m==0)
return 1;
if(m==1)
return (a%n);
ll w=modular_exp(a,m/2,n);
w=(__int128)w*w%n;
if(m&1)
w=(__int128)w*a%n;
return w;
}
bool Miller_Rabin(ll n)
{
srand(time(NULL));
if(n==2)
return true;
if (n < 2 || !(n & 1))
return false;
for(int i=0;i<count;i++)
{
int a=rand()%(n-2)+2;
if(modular_exp(a,n,n)!=a)
return false;
}
return true;
}
ll qpow_long(ll a, ll n, ll m){
ll res = 1;
while(n) {
if(n & 1)
res = (__int128)res * a % m;
n >>= 1;
a = (__int128)a * a % m;
}
return res % m;
}
int main()
{
int T;
cin >> T;
while(T--) {
ll p;
cin >> p;
ll ret = 1, q = p-2;
while(!Miller_Rabin(q)) {
ret = (__int128)ret * qpow_long(q, p-2, p) % p;
q--;
}
printf("%lld\n", ret);
}
return 0;
}