题意:给你一个素数P,1e9<=P<=1e14,问你小于P的第一个素数Q的阶乘(%P)
题解:
- 首先第一个结论:两相邻素数间的间距不会太大(振民不会)比如样例中的1e9+7的下一相邻素数才距离70位
- 第二个定理:威尔逊定理:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p ),可推出( p -2 )! ≡ 1 ( mod p ) (振民不会)
- 然后瞎搞
前置技能:
- 求逆元:当对一个分式取模时,需对分母求逆元(具体详见费马小定理)
int Mod(int a,int b){
int c=1;
while(b){
if(b%2)
c=c*a%mod;
b=b/2;
a=a*a%mod;
}
return c;
}
b的逆元 Mod(b,mod-2)%mod;
x=a*Mod(b,mod-2)%mod;
- 黑科技·快速乘:因为这道题的数据特别大 1e9<=P<=1e14 所以在 乘的过程中会爆long long ,用KSC所有乘法操作都这样搞会在O(1)内完成每次乘法操作
inline ll ksc(ll x,ll y,ll mod)//据说如果模数过大可能导致精度误差
{
ll tmp=(x*y-(ll)((long double)x/mod*y+1.0e-8)*mod);
return tmp<0 ? tmp+mod : tmp;
}
- 黑科技·判素数:瞎搞判素数O(sqrt(n)) ,但是这个黑科技 O(sqrt(n)/ 3);
int isPrime(ll n)
{ //返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
float n_sqrt;
if(n==2 || n==3) return 1;
if(n%6!=1 && n%6!=5) return 0;
n_sqrt=floor(sqrt((float)n));
for(ll i=5;i<=n_sqrt;i+=6)
{
if(n%(i)==0 | n%(i+2)==0) return 0;
}
return 1;
}
最后完整代码:代码与科技的结合(hh)
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<cmath>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
typedef long long ll;
int isPrime(ll n)
{ //返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
float n_sqrt;
if(n==2 || n==3) return 1;
if(n%6!=1 && n%6!=5) return 0;
n_sqrt=floor(sqrt((float)n));
for(ll i=5;i<=n_sqrt;i+=6)
{
if(n%(i)==0 | n%(i+2)==0) return 0;
}
return 1;
}
inline ll ksc(ll x,ll y,ll mod)//据说如果模数过大可能导致精度误差
{
ll tmp=(x*y-(ll)((long double)x/mod*y+1.0e-8)*mod);
return tmp<0 ? tmp+mod : tmp;
}
ll Mod(ll a,ll b,ll p){
ll c=1;
while(b){
if(b%2)
c=ksc(c,a,p);
b=b/2;
a=ksc(a,a,p);
}
return c;
}
int main(){
int t;
cin>>t;
while(t--){
ll p;
cin>>p;
ll n=p;
while(1){
n--;
if(isPrime(n)) break;
}
ll q=n;
ll tmp=1;
for(ll i=q+1;i<=p-2;i++){
tmp=ksc(tmp,i,p);
}
ll ans=Mod(tmp,p-2,p)%p;
cout<<ans<<endl;
}
return 0;
}