传送门:https://www.luogu.com.cn/problem/P2155
题目大意
- 求 (1 , 2 , …… , n !) 中,与 (m !) 互质的数的个数
Solution
-
对于上述问题,我们可以转换为求:在 (1 , …… , n !) 中,不含小于 (m) 的质数为因子的数的个数
(即:设 (p_1 , p_2 , …… , p_k) 为不大于 (M) 的质数,求在 (1 , …… , n!) 中,不包含因子 (p_1 , p_2 , …… , p_k) 的数的个数) -
则我们可以将 (1) 到 (n!) 分为若干段长度为 (m!) 的数列,其中的每一段中与 (m!) 互质的数的个数等于 (1) 到 (m!) 中与 (m!) 互质的数的个数
那么,我们可以获得如下式子:(ans = frac{n!}{m!} imes varphi{(m!)}) ,而 (varphi{(m!)} = m! imes frac{prod{(p_i-1)}}{prod{p_i}})
则上述公式可以转换为:(ans = n! imes frac{prod{(p_i-1)}}{prod{p_i}}) -
则我们可以在线性时间内将 (n!) 、(prod{p_i-1}) 、(prod{p_i})预处理得到,在每次询问时直接 (O(1)) 查询 (n!) 和 (prod{p_i-1}) ,再用费马小定理结合快速幂求得 (prod{p_i}) 的逆元,相乘即可
Code
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const ll maxn=10000010;
ll t,r,n,m,tot;
ll jie[maxn],f1[maxn],f2[maxn],prime[maxn];
bool vis[maxn];
void Init(ll x){ \线性筛
memset(vis,true,sizeof(vis));
vis[0] = vis[1] = false;
for(int i = 2;i <= x;i++){
if(vis[i])
prime[++tot] = i;
for(int j = 1;j <= tot && i * prime[j] <=x ;j++){
vis[i * prime[j]] = false;
if(i % prime[j] == 0)
break;
}
}
}
inline ll ksm(ll a,ll b,ll p)\快速幂求逆元
{
ll ret=1;
while(b)
{
if(b&1) ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return ret;
}
int main(void)
{
scanf("%lld%lld",&t,&r);
Init(maxn-5);
jie[1]=f1[1]=f2[1]=1;
for(int i=2;i<=maxn-5;i++) \预处理
{
jie[i]=jie[i-1]*i%r;
if(vis[i])
{
f1[i]=f1[i-1]*(i-1)%r;
f2[i]=f2[i-1]*i%r;
}
else
{
f1[i]=f1[i-1];
f2[i]=f2[i-1];
}
}
while(t--)
{
scanf("%lld%lld",&n,&m);
printf("%lld
",(jie[n]*f1[m]%r)*ksm(f2[m],r-2,r)%r);
}
return 0;
}