题目:https://www.luogu.org/problemnew/show/P2155 (luogu)
https://www.lydsy.com/JudgeOnline/problem.php?id=2186 (bzoj)
这题要求在(1,N!)里与M!互质的数的个数。
可以把问题分为两部分。
第一部分就是用欧拉函数 phi( M! ) 求1到M!中与M!互质的数的个数。
第二部分是M!+1到N!的与M!互质的个数。
如果gcd( x+M! , M! )==1;那么肯定有gcd( x+M! , M! )==1 。
所以,只用计算 N!到 M!有多少倍的( N! / M! )就可以算出。
答案就为ans = phi( M! ) * ( N! / M! ) % R 。
但是如何求又是一个问题。
化简式子:
ans = ( N! / M!) * M! ∏p|m[ (p-1) / p ]
也就是ans = N!* ∏( p-1 ) / ∏ p
这时候可以用三个数组来预处理一下。
fa[i] 存1到 i-1 的积。
f1[i]=f1[ i-1 ] (i是合数)
=f1[ i-1 ] * ( i-1 )(i是质数)
f2[i]=f2[ i-1 ](i是合数)
=f2[ i-1 ] * i(i是质数)
再用费马小定理求f2[ ]的乘法逆元。
对于每对n,m,ans=fa[n] * f1[m] * { f2[m] ^ (p-2) }
代码如下。
/************************************************************** Problem: 2186 User: ChrisK Language: C++ Result: Accepted Time:6664 ms Memory:264964 kb ****************************************************************/ #include<bits/stdc++.h> using namespace std; long long t,r,n,m; const int maxn=1e7+10; int prime[maxn>>1];// 开小一点。 bool p[maxn]; long long fa[maxn],f1[maxn],f2[maxn]; inline long long qp(long long x,long long y){//快速幂 long long re=1; while(y){ if(y&1) re=(re*x)%r; x=(x*x)%r; y>>=1; } return re; } void prime3(){//线性求素数。 int tot = 0; memset(p,true,sizeof(p)); prime[0] = prime[1] = false; for(int i = 2;i <=maxn;i++){ if(p[i]) prime[++tot] = i; for(int j = 1;j <= tot && i * prime[j] <maxn;j++){ p[i * prime[j]] = false; if(i % prime[j] == 0) break; } } } int main() { scanf("%lld%lld",&t,&r); prime3(); fa[1]=f1[1]=f2[1]=1; for(int i=2;i<=maxn;i++){ fa[i]=(fa[i-1]*i)%r; if(p[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 ",(((fa[n]*f1[m])%r)*qp(f2[m],r-2)%r)); } // while(1); return 0; }