HDU 6608 - Fansblog 2019多校联赛 第三场

                                              Fansblog

                       Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
                                      Total Submission(s): 2107    Accepted Submission(s): 868

 

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,我们需要找出小于Q的最大的质数Q,然后求出Q!modP,进行输出即可;

 

解题分析:

题目上给的P的范围是(1e9≤p≤1e14)围是一看这个范围,肯定就不能用普通的方法啦,首先判断素数和素数打表就是个问题,所以说,在这里就出现了一个新的定理,就是Miller-rabin素数测试方法,可以判断特别大的数,就算是1e14的数,也是可以很快判断出来的,所以我们在这里需要用Miller-rabin去求出这个Q

还有一个知识点,就是威尔逊定理,他内容是这样的:

所以我们代入Q!,有:

即:

但是这里需要注意,因为p-1是取模后的结果,而且数比较大,进行除法以后,精度不允许,而且计算会出错,所以我们这里只需要用p-1去乘以q+1、q+2....p-1的逆元,然后取模即可;

其中逆元的求法,用到的是费马小定理,得出n的逆元就是nmod-2次方,所以就需要用到快速幂哦;

然后其实这里用到快速幂的话,也是有可能会超时的,所以需要用快速乘来对快速幂进行进一步的优化;

 

代码篇:

#include <iostream>
#define ll long long
using namespace std;
const int N = 1e7 + 10;
ll  p, q, mod;
int prime[N + 10], cnt;
bool vis[N + 10];

bool is_prime(ll n)     ///Miller-Rabin判断素数
{
    for(ll i = 0; i < cnt && (ll)prime[i] * prime[i] <= n; i++)
    {
        if(n % prime[i] == 0)
            return false;
    }
    return true;
}

void get_prime()        ///素数打表
{
    for(ll i = 2; i <= N; i++)
    {
        if(!vis[i])
            prime[cnt++] = i;
        for(ll j = 0; j < cnt && i * prime[j] <= N; j++)
        {
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0)
                break;
        }
    }
}

ll mul(ll res, ll k)    ///快速乘
{
    ll ans = 0;
    while(k)
    {
        if(k & 1)
            ans = (ans + res) % mod;
        res = (res + res) % mod;
        k >>= 1;
    }
    return ans;
}

ll poww(ll a, ll b)     ///快速幂
{
    ll ans = 1;
    while(b)
    {
        if(b & 1)
            ans = mul(ans, a);
        a = mul(a, a);
        b >>= 1;
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);    ///优化cin cout
    ll t, ans;
    get_prime();        ///素数打表
    cin >> t;
    while(t--)
    {
        cin >> p;
        ans = p - 1;
        mod = p;
        q = p - 1;
        while(!is_prime(q))     ///求出q的值
            q--;
        for(ll i = q + 1; i < p; i++)       ///乘上对应的逆元
            ans = mul(ans, poww(i, mod - 2));
        cout << ans << endl;
    }
    return 0;
}

 

OVER!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值