写一下最近做的题目。
https://www.luogu.org/problem/P2155
求 [ 1 , n ! ] [1,n!] [1,n!]内与 m ! m! m!互质的数的个数。
这个转化还是比较妙的。
考虑 [ 1 , m ! ] [1,m!] [1,m!]内与 m ! m! m!互质的数的个数,显然答案为 ϕ ( m ! ) \phi(m!) ϕ(m!)。
考虑 ( m ! , n ! ] (m!,n!] (m!,n!]内与 m ! m! m!互质的数的个数。
考虑更相减损术: gcd ( a , b ) = gcd ( a + b , b ) \gcd(a,b)=\gcd(a+b,b) gcd(a,b)=gcd(a+b,b),因此想到 gcd ( a , m ! ) = gcd ( a + m ! , m ! ) = 1 \gcd(a,m!)=\gcd(a+m!,m!)=1 gcd(a,m!)=gcd(a+m!,m!)=1,因此相当于将 ϕ ( m ! ) \phi(m!) ϕ(m!)翻 n ! m ! \frac{n!}{m!} m!n!倍。
综上 a n s = n ! m ! ϕ ( m ! ) ans=\frac{n!}{m!}\phi(m!) ans=m!n!ϕ(m!)。
怎么求呢。
n ! m ! ϕ ( m ! ) \frac{n!}{m!}\phi(m!) m!n!ϕ(m!)
= n ! m ! m ! ∏ i = 1 k ( 1 − 1 p i ) =\frac{n!}{m!}m!\prod_{i=1}^{k}(1-\frac{1}{p_i}) =m!n!m!i=1∏k(1−pi1)
= n ! ∏ i = 1 k p i − 1 p i =n!\prod_{i=1}^{k}\frac{p_i-1}{p_i} =n!i=1∏kpipi−1
这个又怎么做呢?
n ! n! n!直接预处理即可。
设 f n = ∏ i = 1 k p − 1 p f_n=\prod_{i=1}^{k}\frac{p-1}{p} fn=∏i=1kpp−1
后面的考虑我们已经求出了 f a f_a fa,那么 f a ∗ p r i m e = f a ∗ p r i m e − 1 p r i m e f_{a*prime}=f_a*\frac{prime-1}{prime} fa∗prime=fa∗primeprime−1,由于我们可以预处理逆元,因此可以 Θ ( 1 ) \Theta(1) Θ(1)转移。
因为 p i p_i pi的指数必须为 1 1 1,因此我们可以从小到大来做。
实测 n < r n
#include
#include
#include
#define LL long long
#define R register
using namespace std;
int n,m,mod;
int prime[1000010],fac[10000010],f[10000010],inv[10000010];
bool bz[10000010];
void init(int ma)
{
int t=0;
bz[0]=bz[1]=true;
for(R int i=2;i<=ma;i++)
{
if(!bz[i]) prime[++t]=i;
for(R int j=1;j<=t&&i*prime[j]<=ma;j++)
{
bz[i*prime[j]]=true;
if(!(i%prime[j])) break;
}
}
fac[0]=1;
for(R int i=1;i<=ma;i++)
fac[i]=(LL)fac[i-1]*i%mod;
inv[0]=inv[1]=1;
for(R int i=2;i<=min(ma,mod-1);i++)
inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;
f[1]=1;
for(R int i=2;i<=ma;i++)
{
f[i]=f[i-1];
if(!bz[i]) f[i]=(LL)f[i]*inv[i]%mod*(i-1)%mod;
}
}
int main()
{
int T;
scanf("%d %d",&T,&mod);
init(10000000);
while(T--)
{
scanf("%d %d",&n,&m);
printf("%d\n",(LL)fac[n]*f[m]%mod);
}
}