问题虫洞:F - Fansblog HDU - 6608
黑洞内窥:
t组样例,每组给出一个质数P (1e9 <= P <= 1e14)
然后你要自己求P的前一个质数Q,
求 (Q! % P)
思维光年:
鬼会想到威尔逊哦~~~~ ~~~~~~
那就先介绍一下威尔逊定理吧!
威尔逊定理(百度百科)
在初等数论中,威尔逊定理给出了判定一个自然数是否为素数的充分必要条件。
即:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p ),但是由于阶乘是呈爆炸增长的,其结论对于实际操作意义不大。
(这里的-1 其实是 p-1.)
好了,威尔逊定理就是上面的那个公式了,现在我们可以来推一下这道题了。。
推理过程:
由威尔逊定理可知:
(P-1) ! mod P = P-1
则有:(Q < P)
Q!* (Q+1)*(Q+2)*......*(P-1) = (P-1)!
继续推导:
最后得到:
有人可能会问,两个质数相差太大不久会暴掉或者T掉嘛。。。。
这个,,我也不知道为什么,两个质数之间会相隔的怎么近
还有就是,记得要用快速乘法,不然会wa,,,,,
ACcode:
//#include<bits/stdc++.h>
//std::ios::sync_with_stdio(false);
#include <stdio.h>
#include <iostream>
#include<algorithm>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <math.h>
#include <vector>
#include <cstring>
#include <stdlib.h>
#include <string.h>
using namespace std;
typedef long long ll;
#define MAXN 3000005
#define INF 0x3f3f3f3f//将近ll类型最大数的一半,而且乘2不会爆ll
//const ll mod = 998244353;
#define mem(a, b) memset(a, b, sizeof(a))
ll mod;
ll mul(ll a, ll b)//快速乘法
{
ll res = 0;
while(b)
{
if(b&1)
res = (res+a)%mod;
a = (a+a)%mod;
b >>= 1;
}
return res%mod;
}
ll ksm(ll a, ll b)
{
ll ans = 1;
while(b)
{
if(b&1)
ans = mul(ans, a);
a = mul(a, a);
b >>= 1;
}
return ans;
}
bool prime(ll x)
{
for(ll i=2; i*i<=x; ++i)
if(x%i == 0) return 0;
return 1;
}
int main()
{
ll t, n;
cin >> t;
while(t--)
{
scanf("%lld", &n);
mod = n;
ll q = n-1;
while(!prime(q)) q--;
ll ans = n-1;
for(ll i=q+1; i<=n-1; ++i)
ans = mul(ans, ksm(i, mod-2));
cout << ans << '\n';
}
return 0;
}
好吧,可能有人总觉得根号n的查找 太暴力了,那么我们再用米勒拉宾素数判定优化一下八:
//#include<bits/stdc++.h>
//std::ios::sync_with_stdio(false);
#include <stdio.h>
#include <iostream>
#include<algorithm>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <string>
#include <math.h>
#include <vector>
#include <cstring>
#include <stdlib.h>
#include <string.h>
using namespace std;
typedef long long ll;
#define MAXN 3000005
#define INF 0x3f3f3f3f//将近ll类型最大数的一半,而且乘2不会爆ll
//const ll mod = 998244353;
#define mem(a, b) memset(a, b, sizeof(a))
ll mul(ll a, ll b, ll md)
{
ll res = 0;
while(b)
{
if(b&1)
res = (res+a)%md;
a = (a+a)%md;
b >>= 1;
}
return res%md;
}
ll ksm(ll a, ll b, ll md)
{
ll ans = 1;
while(b)
{
if(b&1)
ans = mul(ans, a, md);
a = mul(a, a, md);
b >>= 1;
}
return ans;
}
bool Miller_Rabbin(ll n,ll a) //米勒拉宾素数判断函数主体
{
ll d=n-1,s=0,i;
while(!(d&1)) // 先把(2^s)*d 算出来
{
d>>=1;
s++;
}
ll t=ksm(a, d, n); //a^d取一次余判断
if(t==1 || t==-1) //一或负一则可以声明这可能是质数
return 1;
for(i=0; i<s; i++) //不是的话继续乘上s个2
{
if(t==n-1) //(n-1)*(n-1)%n=1 这一步是优化
return 1;
t=mul(t, t, n); // 快乘
}
return 0;
}
bool is_prime(ll n)
{
ll i,tab[4]= {3,4,7,11}; //本来应该取[1,n]内任意整数
for(i=0; i<4; i++) //但一般这几个数足以,不需要太多组测试
{
if(n==tab[i])
return 1; //小判断小优化~
if(!n%tab[i])
return 0;
if(n>tab[i] && !Miller_Rabbin(n,tab[i]))
return 0;
}
return 1;
}
int main()
{
ll t, n;
cin >> t;
while(t--)
{
scanf("%lld", &n);
ll mod = n;
ll q = n-1;
while(!is_prime(q))
q--;
ll ans = n-1;
for(ll i=q+1; i<=n-1; ++i)
ans = mul(ans, ksm(i, mod-2, mod), mod);
cout << ans << '\n';
}
return 0;
}