大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。
第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模后面T行,每行一对整数N,M,见题目描述 m<=n
共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值
对于100%的数据,1 < = N , M < = 10000000
首先很显然,答案等于 n!m!∗ϕ(m!)
模数是提前给出的,所以可以先预处理出 ϕ(1!)ϕ(2!)—ϕ(107!) ,以及1到 107 的阶乘及其的逆元。
首先线性筛出范围内的素数,然后已经求出 ϕ((m−1)!) ,现在要求 ϕ(m!) 。根据欧拉函数的性质,如果m为素数,那么 ϕ(m!)=phi((m−1)!)∗(m−1) ,否则 ϕ(m!)=ϕ((m−1)!)∗m
用扩展欧几里得或快速幂会超时,要先线性求1—— 107 的逆元,然后像阶乘一样乘在一起即可。 线性求逆元方法:
Inv[1]=1; for (int i=2;i<=nn;i++) Inv[i]=(LL)Inv[mo%i]*(mo-(mo/i))%mo;然后询问O(1)解决
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int maxn=10000005,N=1e7; typedef long long LL; int T,mo,n,m,ans,nn,phi[maxn],tot,pr[maxn],fact[maxn],Inv[maxn],ii[maxn]; bool bz[maxn]; void init() { for (int i=2;i<=nn;i++) { if (!bz[i]) pr[tot++]=i; for (int j=0;j<tot && i*pr[j]<=nn;j++) { bz[i*pr[j]]=1; if (i % pr[j]==0) break; } } Inv[1]=1; for (int i=2;i<=nn;i++) Inv[i]=(LL)Inv[mo%i]*(mo-(mo/i))%mo; fact[0]=ii[0]=1; for (int i=1;i<=nn;i++) { fact[i]=(LL)fact[i-1]*i%mo; ii[i]=(LL)ii[i-1]*Inv[i]%mo; } phi[1]=1; phi[2]=1; for (int i=3;i<=N;i++) { if (!bz[i]) phi[i]=(LL)phi[i-1]*(i-1)%mo; else phi[i]=(LL)phi[i-1]*i%mo; } } int main() { scanf("%d%d",&T,&mo); nn=min(N,mo); init(); while (T--) { scanf("%d%d",&n,&m); ans=(LL)phi[m]*fact[n]%mo*ii[m]%mo; printf("%d\n",ans); } return 0; }