题目链接:戳我
显然,如果\(n==m\)的时候,我们求的是\(\phi(m)\)
而现在\(n>=m\),那么\(n!\)一定是\(m!\)的整数倍,每个\(m!\)对答案的贡献为\(\phi(m!)\)
所以题目转化成求
\(\frac{n!}{m!}\phi(m!)\)
\(=\frac{n!}{m!}\frac{\prod (p_i-1)}{\prod p_i}\)
其中\(p_i\)是小于m的所有质数(因为如果求\(\phi(x)\),那些\(p_i\)就是它的质因子,而对于阶乘来说,显然就是小于它的所有质数)
\(=n!\frac{\prod(p_i-1)}{\prod p_i}\)
对于这题的数据范围,显然是每次询问的时间复杂度必须要在\(O(logn)\)及以内,所以我们要将上面的值进行预处理。
不能逆着推这道题的逆元!因为没有保证与模数互质QAQ,所以询问的时候直接快速幂求一下就好了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define MAXN 10000000
using namespace std;
int T,mod,n,m,cnt;
int fn[MAXN+10],fv[MAXN+10],prime[MAXN+10],id[MAXN+10],done[MAXN+10],fc[MAXN+10];
inline int fpow(int x,int y)
{
int cur_ans=1;
while(y)
{
if(y&1) cur_ans=1ll*cur_ans*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return cur_ans;
}
inline void init()
{
fc[0]=1;
done[1]=1;
for(int i=1;i<=MAXN;i++) fc[i]=1ll*fc[i-1]*i%mod;
for(int i=2;i<=MAXN;i++)
{
if(!done[i]) prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=MAXN;j++)
{
done[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
fv[0]=fn[0]=1;
// for(int i=1;i<=100;i++) printf("prime[%d]=%d\n",i,prime[i]);
for(int i=1;i<=cnt;i++) fv[i]=1ll*fv[i-1]*(prime[i]-1)%mod;
for(int i=1;i<=cnt;i++) fn[i]=1ll*fn[i-1]*prime[i]%mod;
for(int i=1;i<=cnt;i++)
for(int j=prime[i];j<prime[i+1];j++)
id[j]=i;
// for(int i=1;i<=100;i++) printf("id[%d]=%d\n",i,id[i]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&T,&mod);
init();
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",1ll*fc[n]*fv[id[m]]%mod*fpow(fn[id[m]],mod-2)%mod);
}
return 0;
}