hdu多校第三场 1006 (hdu6608) Fansblog Miller-Rabin素性检测

Posted isakovsky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu多校第三场 1006 (hdu6608) Fansblog Miller-Rabin素性检测相关的知识,希望对你有一定的参考价值。

题意:

给你一个1e9-1e14的质数P,让你找出这个质数的前一个质数Q,然后计算Q!mod P

题解:

1e9的数据范围pass掉一切素数筛法,考虑Miller-Rabin算法。

米勒拉宾算法是一种判断素数的随机化算法,由于其随机性,它不能保证总是正确的,但其对于一个素数,总会返回素数的结果,对于一个合数,才有极小概率返回素数的结果(假阳性)。

米勒拉宾算法对于单个素数的判断时间复杂度为$O(log^3n)$.(因为1e14相乘会爆longlong,模乘要写成龟速乘,因此要多一个log)

1849年,高斯猜想,素数分布密度符合如下的公式,$π(x)≈x/lnx$,其中$π(x)$为不超过x的素数个数,根据这个公式,1e14以内,两个素数间隔平均只有46个左右,相当稠密了。

因此,只需要用米勒拉宾算法从P-1一个一个判断,直到找到另一个素数Q。

再给出一个结论,对于任意素数n,(n-2)! mod n = 1,因为从2到n-2,互为模n逆元的数捉对出现,证明可参考离散数学课本数论部分。

因此,计算Q! mod p只需计算$\pi^P-2_Q+1$再利用费马小定理快速幂求逆元即可。

需要注意,1e14相乘会爆longlong,可以用_int128,但稳妥一点的方法是用思想类似于快速幂的龟速乘。

#include<bits/stdc++.h>
#define Times 10 
#define LL long long
#define ll long long
using namespace std;

ll multi(ll a, ll b, ll m) 
    ll ans = 0; a %= m;
    while (b) 
        if (b & 1)ans = (ans + a) % m;
        a = (a + a) % m; b >>= 1;
     return ans;


ll quick_mod(ll a, ll b, ll m) 
    ll ans = 1; a %= m;
    while (b) 
        if (b & 1)ans = multi(ans, a, m);
        a = multi(a, a, m); b >>= 1;
     return ans;


bool Miller_Rabin(ll n) 
    if (n == 2)return true;
    if ((n < 2) || !(n & 1))return false;
    ll m = n - 1;
    ll k = 0;
    while ((m & 1) == 0) 
        k++; m >>= 1;
    
    for (ll i = 0; i < Times; i++) 
        ll a = rand() % (n - 1) + 1;
        ll x = quick_mod(a, m, n);
        ll y = 0;
        for (ll j = 0; j < k; j++) 
            y = multi(x, x, n);
            if (y == 1 && x != 1 && x != n - 1)return false;
            x = y;
         if (y != 1)return false;
     return true;


long long quick_mul(long long x,long long y,long long mod) 

    long long ans=0;
    while(y!=0)
        if(y&1==1)ans+=x,ans%=mod;
        x=x+x,x%=mod;
        y>>=1; 
    
    return ans;

long long quick_pow(long long x,long long y,long long mod)

    long long sum=1;
    while(y!=0)
         if(y&1==1)sum=quick_mul(sum,x,mod),sum%=mod;
             x=quick_mul(x,x,mod),x%=mod;
              y=y>>1;
    
    return sum;


int main()
//    LL pp=1;
//    for(int i=1;i<=999999937;i++)
//        pp=pp*i%1000000007;
//     
//    printf("%lld\n",pp);
    int t;
    scanf("%d",&t);
    while(t--)
        LL q;
        scanf("%lld",&q);
        LL p;
        for(register LL i=q-2;;i-=2)
            if(Miller_Rabin(i))
                p=i;break;
            
        
//        printf("%d\n",p);
        LL ans=1;
        
        for(LL j=p+1;j<=q-2;j++)
            ans=quick_mul(ans,j,q);
        
        printf("%lld\n",quick_pow(ans,q-2,q));
    
    return 0;

 

以上是关于hdu多校第三场 1006 (hdu6608) Fansblog Miller-Rabin素性检测的主要内容,如果未能解决你的问题,请参考以下文章

《HDU多校第三场》

hdu多校第三场 1007 (hdu6609) Find the answer 线段树

2021HDU多校第三场 1009 Rise in Price 思维+乱搞

2020hdu多校第三场1005(6795)Little W and Contest

2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)

2019杭电多校第三场 1004 Distribution of books