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多校第三场 1007 (hdu6609) Find the answer 线段树
2021HDU多校第三场 1009 Rise in Price 思维+乱搞
2020hdu多校第三场1005(6795)Little W and Contest