HDU 6608 [2019 Multi-University Training Contest 3]

Fansblog

Problem Description

Farmer John keeps a website called ‘FansBlog’ .Everyday , there are many people visited this blog.One day, he find the visits has reached P , which is a prime number.He thinks it is a interesting fact.And he remembers that the visits had reached another prime number.He try to find out the largest prime number Q ( Q < P ) ,and get the answer of Q! Module P.But he is too busy to find out the answer. So he ask you for help. ( Q! is the product of all positive integers less than or equal to n: n! = n * (n-1) * (n-2) * (n-3) *… * 3 * 2 * 1 . For example, 4! = 4 * 3 * 2 * 1 = 24 )

Input

First line contains an number T(1<=T<=10) indicating the number of testcases.
Then T line follows, each contains a positive prime number P (1e9≤p≤1e14)

Output

For each testcase, output an integer representing the factorial of Q modulo P.

Sample Input

1
1000000007

Sample Output

328400734

思路

题目的意思是,给出一个素数P,让我们找出小于n的最大素数Q。首先数字非常大,10的9次方~10的14次方,不可能暴力判断素性,百度发现有一种算法叫做:米勒-拉宾素性检验 ,据说在1014以内最大素数间隔是777,那么直接从给出的素数往前逐个进行米勒拉宾素性检验就行了。找到需要的Q之后,发现需要阶乘,即使是范围内最小的数两个相乘也会炸,所以需要别的办法。
根据威尔逊定理,当P为质数,有 (P − 1) ! ≡ P − 1(mod P),则有:
(P−1) (P−2)…(Q+1) Q ! ≡ P − 1 (mod P) (P - 1) (P - 2)…(Q + 1) Q ! ≡ P − 1 (mod P)
那么只需要求得(P − 1) (P − 2)…(Q + 1) (P - 1)(P - 2)…(Q + 1) (P − 1) (P − 2)…(Q + 1) 在mod P下的逆元,再乘以P − 1,即可得到Q !
当然了,上面说的,我一点都不懂,全是复制的,所以只能在这里贴一下kuangbin模板,然后借鉴大佬的代码把后面所需的阶乘取模改出来了。

Miller_Rabin 算法进行素数测试模板

//特点:速度快、可以判断一个 < 2^63 的数是不是素数 
const int S = 8;  //随机算法判定次数一般 8~10 就够了

//计算 ret = (a * b) % c	a,b,c < 2^63
ll mult_mod(ll a, ll b, ll c) {
	a %= c;
	b %= c;
	ll ret = 0;
	ll tmp = a;
	while(b) {
		if(b & 1) {
			ret += tmp;
			if(ret > c) {
				ret -= c;  //直接取模慢很多
			}
		}
		tmp <<= 1;
		if(tmp > c) {
			tmp -= c;
		}
		b>>=1;
	}
	return ret;
}

//计算 ret = (a^n) % mod
ll pow_mod(ll a, ll n, ll mod) {
	ll ret = 1;
	ll temp = a % mod;
	while(n) {
		if(n & 1) {
			ret = mult_mod(ret,temp,mod);
		}
		temp = mult_mod(temp,temp,mod);
		n >>= 1;
	}
	return ret;
}

//通过a^(n-1)=1(mod n)来判断 n 是不是素数
//n-1 = x*2^t 中间使用二次判断
//是合数返回true,不一定是合数返回false
bool check(ll a, ll n, ll x, ll t) {
	ll ret = pow_mod(a,x,n);
	ll last = ret;
	for(int i=1; i<=t; i++) {
		ret = mult_mod(ret,ret,n);
		if(ret == 1 && last != 1 &&last != n-1) {
			return true;
		}
		last = ret;
	}
	if(ret != 1) {
		return true;
	} else {
		return false;
	}
}

//Miller_Rabin算法 
//是素数返回true(可能是伪素数) 
//不是素数返回false 
bool Miller_Rabin(ll n) {
	if(n < 2) {
		return false;  //1、0和负数都不是素数 
	}
	if(n == 2) {
		return true;  //2是素数 
	}
	if((n & 1) == 0) {
		return false;  //除了2的偶数都不是素数 
	}
	ll x = n - 1;
	ll t = 0;
	while((x & 1) == 0) {
		x >>= 1;
		t++;
	}
	srand(time(NULL));
	for(int i=0; i<S; i++) {
		ll a = rand() % (n-1) + 1;
		if(check(a,n,x,t)) {
			return false;
		}
	}
	return true;
}

本题代码

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 0+5;
typedef long long ll;

ll t,p,q;

const int S = 8;  //随机算法判定次数一般 8~10 就够了

//计算 ret = (a * b) % c	a,b,c < 2^63
ll mult_mod(ll a, ll b, ll c) {
	a %= c;
	b %= c;
	ll ret = 0;
	ll tmp = a;
	while(b) {
		if(b&1) {
			ret += tmp;
			if(ret > c) {
				ret -= c;  //直接取模慢很多
			}
		}
		tmp <<= 1;
		if(tmp > c) {
			tmp -= c;
		}
		b >>= 1;
	}
	return ret;
}

//计算 ret = (a^n) % mod
ll pow_mod(ll a, ll n, ll mod) {
	ll ret = 1;
	ll temp = a % mod;
	while(n) {
		if(n&1) {
			ret = mult_mod(ret,temp,mod);
		}
		temp = mult_mod(temp,temp,mod);
		n >>= 1;
	}
	return ret;
}

//通过a^(n-1)=1(mod n)来判断 n 是不是素数
//n-1 = x*2^t 中间使用二次判断
//是合数返回true,不一定是合数返回false
bool check(ll a, ll n, ll x, ll t) {
	ll ret = pow_mod(a,x,n);
	ll last = ret;
	for(int i=1; i<=t; i++) {
		ret = mult_mod(ret,ret,n);
		if(ret == 1 && last != 1 && last != n-1) {
			return true;
		}
		last = ret;
	}
	if(ret != 1) {
		return true;
	} else {
		return false;
	}
}

//Miller_Rabin算法
//是素数返回true(可能是伪素数)
//不是素数返回false
bool Miller_Rabin(ll n) {
	if(n < 2) {
		return false;  //1、0和负数都不是素数
	}
	if(n == 2) {
		return true;  //2是素数
	}
	if((n&1) == 0) {
		return false;  //除了2的偶数都不是素数
	}
	ll x = n - 1;
	ll t = 0;
	while((x&1) == 0) {
		x >>= 1;
		t++;
	}
	srand(time(NULL));
	for(int i=0; i<S; i++) {
		ll a = rand() % (n-1) + 1;
		if(check(a,n,x,t)) {
			return false;
		}
	}
	return true;
}

ll solve() {
	if(q == p-2) {
		return 1;
	}
	ll x = 1;
	for(ll i=q+1; i<=p-2; i++) {
		x = mult_mod(x,i,p);
	}
	return 1l * pow_mod(x,p-2,p);
}

int main() {
	while(cin >> t) {
		while(t--) {
			cin >> p;
			for(ll i=p-1; i>0; i--) {
				if(Miller_Rabin(i)) {
					q = i;
					break;
				}
			}
			cout << solve() << endl;
		}
	}
}

题目来源

HDU 6608 Fansblog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值