题意:
给你一个1e9-1e14的质数P,让你找出这个质数的前一个质数Q,然后计算Q!mod P
题解:
1e14的数据范围pass掉一切素数筛法,考虑Miller-Rabin算法。
米勒拉宾算法是一种判断素数的随机化算法,由于其随机性,它不能保证总是正确的,但其对于一个素数,总会返回素数的结果,对于一个合数,才有极小概率返回素数的结果(假阳性)。
米勒拉宾算法对于单个素数的判断时间复杂度为$O(log^3n)$.(因为1e14相乘会爆longlong,模乘要写成龟速乘,因此要多一个log)
1849年,高斯猜想,素数分布密度符合如下的公式,$pi(x) approx x/lnx$,其中$pi(x)$为不超过x的素数个数,根据这个公式,1e14以内,两个素数间隔平均只有46个左右,相当稠密了。
因此,只需要用米勒拉宾算法从P-1一个一个判断,直到找到另一个素数Q。
再给出一个结论,对于任意素数n,(n-2)! mod n = 1,因为从2到n-2,互为模n逆元的数捉对出现,证明可参考离散数学课本数论部分。
因此,计算Q! mod P只需计算 $prod^{P-2}_{i=Q+1}i$ 再利用费马小定理快速幂求逆元即可。
需要注意,1e14相乘会爆longlong,可以用_int128,但稳妥一点的方法是用思想类似于快速幂的龟速乘。
#include<bits/stdc++.h> #define Times 10 #define LL long long #define ll long long using namespace std; ll multi(ll a, ll b, ll m) { ll ans = 0; a %= m; while (b) { if (b & 1)ans = (ans + a) % m; a = (a + a) % m; b >>= 1; } return ans; } ll quick_mod(ll a, ll b, ll m) { ll ans = 1; a %= m; while (b) { if (b & 1)ans = multi(ans, a, m); a = multi(a, a, m); b >>= 1; } return ans; } bool Miller_Rabin(ll n) { if (n == 2)return true; if ((n < 2) || !(n & 1))return false; ll m = n - 1; ll k = 0; while ((m & 1) == 0) { k++; m >>= 1; } for (ll i = 0; i < Times; i++) { ll a = rand() % (n - 1) + 1; ll x = quick_mod(a, m, n); ll y = 0; for (ll j = 0; j < k; j++) { y = multi(x, x, n); if (y == 1 && x != 1 && x != n - 1)return false; x = y; } if (y != 1)return false; } return true; } long long quick_mul(long long x,long long y,long long mod) { long long ans=0; while(y!=0){ if(y&1==1)ans+=x,ans%=mod; x=x+x,x%=mod; y>>=1; } return ans; } long long quick_pow(long long x,long long y,long long mod) { long long sum=1; while(y!=0){ if(y&1==1)sum=quick_mul(sum,x,mod),sum%=mod; x=quick_mul(x,x,mod),x%=mod; y=y>>1; } return sum; } int main(){ // LL pp=1; // for(int i=1;i<=999999937;i++){ // pp=pp*i%1000000007; // } // printf("%lld ",pp); int t; scanf("%d",&t); while(t--){ LL q; scanf("%lld",&q); LL p; for(register LL i=q-2;;i-=2){ if(Miller_Rabin(i)){ p=i;break; } } // printf("%d ",p); LL ans=1; for(LL j=p+1;j<=q-2;j++){ ans=quick_mul(ans,j,q); } printf("%lld ",quick_pow(ans,q-2,q)); } return 0; }