原題鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6608
題目大意:給你一個質數p;讓你找到比p小的最大質數q,然後求出q的階乘模p的結果。
解題思路:首先我們需要知道威爾遜定理:
對於一個質數p,p-1的階乘模除以p等於p-1;
(p-1)! %p=(p-1);(很nb)。
我們要求的是q的階乘模除以p,所以我們可以得到以下式子:
q! * (q+1) * (q+2) * … *(p-3) * (p-2) * (p-1) % p = (p-1);
而我們可以把(q+1) * (q+2) * … *(p-3) * (p-2) * (p-1) % p除過去,就變成了:
q! = (p-1) * inv(p-1) * inv(p-2) * inv(p-3) * … * inv(q+2) * inv(q+1)。
inv表示逆元。解出來就是答案。
但還有一點要注意,因爲p的範圍在1e9到1e14,兩個(p-1)相乘會爆long long,所以需要用到快速乘,(現學現用)。
大致就這些,詳細看Code:
#include<iostream>
using namespace std;
typedef long long ll;
ll prime[5] = {2,5,3,233,331};
bool check(ll x){//檢測是否爲素數
if(x==1||x==0) return 0;
if(x==2) return 1;
for(ll i=3;i*i<x;i++){
if(x%i==0) return 0;
}
return 1;
}
ll ksc(ll a,ll b,ll p){//傳說中的快速乘
return (a*b-(ll)((long double)a/p*b)*p+p)%p;
}
ll qpow(ll a,ll b,ll p){//這個就是快速冪的至尊版,需要搭配快速乘使用
ll res=1;
while(b){
if(b&1) res=ksc(res,a,p);
b>>=1;
a=ksc(a,a,p);
}
return res;
}
bool Miller_Rabin(ll p)//這個是超級無敵秀的 米勒羅賓素數檢測模板
{ //比上面那個素數檢測快了好多倍
if(p < 2) return 0;//很顯然我是cv過來的
if(p != 2 && p % 2 == 0) return 0;
ll s = p - 1;
while(! (s & 1)) s >>= 1;
for(int i = 0; i < 3; ++i)
{
if(p == prime[i]) return 1;
ll t = s, m = qpow(prime[i], s, p);
while(t != p - 1 && m != 1 && m != p - 1)
{
m = ksc(m, m, p);
t <<= 1;
}
if(m != p - 1 && !(t & 1)) return 0;
}
return 1;
}
ll inv(ll x,ll p){//求解一個數的逆元
return qpow(x,p-2,p);
}
int main(){
int t;
cin>>t;
while(t--){
ll p,ans;
cin>>p;
ans=p-1;
for(ll x=p-2;x>=1;x-=2){
if(Miller_Rabin(x)){//這裏也可以換成check(x)檢測
for(ll j=x+1;j<p;j++){
ans=ksc(ans,inv(j,p),p);
}
cout<<ans<<endl;
break;
}
}
}
return 0;
}