HDU - 6608 Fansblog 费马小定理+威尔逊定理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6608


题目大意:给定一个素数P, 求 (Q !)%P , Q是小于P的最大素数


先说说两个定理

威尔逊定理:

当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p ),是判断p是否是素数的充要条件。

费马小定理

如果p是一个质数,而整数a不是p的倍数,则有a(p-1) ≡ 1(mod p)。

再说一下逆元的概念

逆元

已知P为质数,且A和P互质, 如果A * B ≡ 1(mod p)因此在%P的情况下,除A就等于乘B,B就是A的逆元。

思路

  首先根据威尔逊定理,我们可以知道 ( P -1 )! ≡ -1 ( mod P ),即:

( P -1 )! ≡ P-1 ( mod P )

因此不妨先将答案置为P -1,再除去Q+1到P-1的数的乘积。
  接下来先找Q,这里Q直接从P-1开始暴力往下找(本来想用的筛法,但是P太大了,只能从P-1往下找)挨个判断是否是素数,直到找到第一个素数,就是Q了,接下来就挨个除去Q+1到P-1的数(乘上他们%P条件下的逆元),由费马小定理可以知道 对于 Q-1<=X<=P-1 ,X满足X(p-1) ≡ 1(mod p),即:

X*X(p-2) ≡ 1(mod p)

所以X的逆元就是X(p-2) (%P条件下),X(p-2) 的值用快速幂求出,注意在快速幂中相乘那一块可能会溢出long long,应该先强制转化乘long double处理。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <string>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define ms(a, x) memset(a, x, sizeof(a))
#define fore(i, a, n) for (long long i = a; i < n; i++)
#define ford(i, a, n) for (long long i = n - 1; i >= a; i--)
#define si(a) scanf("%d", &a)
#define sl(a) scanf("%lld", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define sll(a, b) scanf("%lld%lld", &a, &b)
#define slll(a, b, c) scanf("%lld%lld%lld", &a, &b, &c)
#define debug(a) cout << a << endl
#define pr(a) printf("%d ",a)
#define endl '\n'
#define pi acos(-1.0)
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define ull unsigned long long
#define ll long long
const double eps = 1e-8;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
const int inf=0x3f3f3f3f;
const int MAXN = 5e5+5;
ll Mo(ll a, ll b ,ll mode)
{
	//这里会溢出long long,所以一定要用long double处理
    return (a*b-(ll)((long double)a*b/mode)*mode+mode)% mode;
}
ll Qpow(ll a,ll b,ll mode)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=Mo(ans,a,mode);
        b>>=1;
        a=Mo(a,a,mode);
    }
    return ans;
}
bool check(ll x)
{
    for(ll i=2;i*i<=x;++i)
    {
        if(x%i==0)
        return false;
    }
    return true;
}
void solve()
{
    ll p;
    sl(p);
    ll q;
    for(ll i=p-1;;i--)
    {
        if(check(i)){
            q=i;break;
        }
    }
    ll ans=p-1;
    for(ll i=q+1;i<=p-1;++i)
    {
        ans=Mo(ans,(ll)(Qpow(i,p-2,p)),p);
    }
    cout<<ans<<endl;
    return;
}
int main()
{
    int t;
    si(t);
    while(t--)solve();
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值