2019杭电多校第三场 F.Fansblog(威尔逊定理+黑科技)

探讨了素数P范围内第一个素数Q的阶乘模P问题,利用威尔逊定理和快速乘法技巧,介绍了高效求解方法,包括求逆元、快速乘和素数判断的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一个素数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;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值