题目链接:戳我

显然,如果\(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;
}