给你一个素数p,让你求 k!%p, 其中k为比p小的整数里最大的素数。例如p=5,则k=3。p=11,则k=7。 k! = k*(k-1)*······*2*1;
Input第一行包含一个整数 T(1<=T<=10) 表示测试样例的个数.
接下来有T行,每行包含一个素数 p (1e9≤p≤1e14)
Output对于每个测试样例,输出一个整数k!%p,代表答案
Sample Input
1 1000000007
Sample Output
328400734
因为两个素数之间的间隔不会超过300,我们从P-1开始一个个查验找Q。再把(P-1)乘上[Q,P-1]的逆元即可。
注意因为数很大,所有涉及乘的地方都要用快速乘。
威尔逊定理:在初等数论中,威尔逊定理给出了判定一个自然数是否为素数的充分必要条件。
即:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p ),但是由于阶乘是呈爆炸增长的,其结论对于实际操作意义不大。
也就是说(P-1)!mod P == P-1;
//威尔逊定理是(P-1)!modP==P-1
//Q P
//(Q-1)!modP=-1 ==> (P-1)!mod P == P-1;
//Q!*(Q+1)*(Q+2)....(P-1)==(P-1)!
//Q!(mod P)==(P-1)!/[(Q+1)*(Q+2)*(Q+3)....(P-1)](mod P)
//Q!(mod P)==(P-1)/(Q+1)*(Q+2)*(Q+3)*...*(P-1))(mod P)
因为这里的数乘法太大了,所以用的快速乘法
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1e7+100; int prime[maxn]; bool biaoji[maxn]; int cnt=0; void inint(){ for(int i=2;i<maxn;i++){ if(!biaoji[i]) prime[++cnt]=i; for(int j=1;j<=cnt&&i*prime[j]<maxn;j++){ biaoji[i*prime[j]]=1; if(i%prime[j]==0){ break; } } } } bool judge(ll x){ for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;i++){ if(x%prime[i]==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%mod; } ll qpow(ll a,ll b,ll mod){ ll ans=1; while(b){ if(b&1){ ans=mul(ans,a,mod)%mod; } a=mul(a,a,mod)%mod; b>>=1; } return ans%mod; } //威尔逊定理是(P-1)!modP==P-1 //Q P //(Q-1)!modP=-1 ==> (P-1)!mod P == P-1; //Q!*(Q+1)*(Q+2)....(P-1)==(P-1)! //Q!(mod P)==(P-1)!/[(Q+1)*(Q+2)*(Q+3)....(P-1)](mod P) //Q!(mod P)==(P-1)/(Q+1)*(Q+2)*(Q+3)*...*(P-1))(mod P) int main(){ int t; inint(); cin>>t; while(t--){ ll p; cin>>p; ll mod=p; ll q; for(q=p-1;q>=2;q--){ if(judge(q)){ break; } } ll ans=p-1; for(ll i=q+1;i<=p-1;i++){ ans=(mul(ans , qpow(i,mod-2,mod) , mod)); } cout<<ans<<endl; } }