题意:
给定一个大质数P,要求找到一个小于P的最大质数Q,
输出Q的阶乘对P取模的结果。
数据范围:1e9<=P<=1e14
解法:
Q和P之间的距离挺小的(好像比1e5还小很多,但是网上没找到证明)
从P-1开始向下暴力循环找到最大的素数Q,
判断素数我用的筛选+试除法,不过好像直接试除法也可以过.
然后问题剩下计算Q!%P,
先介绍威尔逊定理:
(P-1)!≡-1(mod P),当且仅当P是质数时成立,
右边的负数-1可以加上一个P变为整数P-1,
左右可以同时约掉一个P-1变成:(P-2)!≡1(mod P)
由于Q<P,且显然Q不等于P-1,即Q<=P-2
那么(P-2)!=Q!*(Q+1)...*(P-2),(感叹号表示阶乘)
因此(P-2)!≡1(mod P)可以变为Q!*(Q+1)...*(P-2)≡1(mod P)
将左右同时乘上一个(Q+1)...*(P-2)的逆元,
左边变为Q!,那么右边就是答案了.
ps:
乘法爆longlong,改用快速乘或者用__int128
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=1e7+5;
int notprime[maxm];
int prime[maxm],cnt;
void init(){
for(int i=2;i<maxm;i++){
if(!notprime[i]){
prime[cnt++]=i;
}
for(int j=0;j<cnt;j++){
if(1ll*prime[j]*i>=maxm)break;
notprime[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
bool isprime(ll x){
for(int j=0;j<cnt;j++){
if(x%prime[j]==0)return 0;
}
return 1;
}
ll mul(ll a,ll b,ll mod){
ll ans=0;
while(b){
if(b&1)ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
ll ppow(ll a,ll b,ll mod){
ll ans=1%mod;a%=mod;
while(b){
if(b&1)ans=mul(ans,a,mod);
a=mul(a,a,mod);
b>>=1;
}
return ans;
}
signed main(){
init();
int T;scanf("%d",&T);
while(T--){
ll P;scanf("%lld",&P);
ll Q=P-1;
while(!isprime(Q))Q--;
ll ans=1;
for(ll i=Q+1;i<=P-2;i++){
ans=mul(ans,ppow(i,P-2,P),P);
}
printf("%lld\n",ans);
}
return 0;
}