题解 P2155【[SDOI2008] 沙拉公主的困惑】
Isprime
·
·
题解
题意简述:给定 N,M,R,求 [1,N!] 中与 M! 互质的数的数量。
伟大的欧几里得已经告诉我们该怎么做了。
题目中限定了 M \leq N,那么显然 M! | N!.
常识告诉我们,gcd(a,b)=gcd(a+k\times b,b).然而常识很多时候是假的,所以我们考虑证明这个式子。
设 s=gcd(a,b), 则存在 x,y\in\mathbb{Z}使得 a=x\times s,b=y\times s,gcd(x,y)=1,于是 gcd(a+b\times k,b)=gcd(x\times s+y\times s\times k,y\times s)=gcd(s\times(x+y\times k),s\times y)=s\times gcd(x+y\times k,y).
设 m|(x+y\times k) 且 m|y,所以 m|(y\times k),所以 m|x.即 m|x 且 m|y,又因为 gcd(x,y)=1,所以 gcd(x+y\times k,y)=m=1,所以 gcd(a+b\times k,b)=s\times gcd(x+y\times k,y)=s=gcd(a,b).
这个式子能说明什么呢?我们令 b=M!,显然可以发现:对于 \forall k\in [1,\frac{N!}{M!}],\forall x\in[1,M!] 有 gcd(k*M!+x,M!)=gcd(x,M!).
也就是说,对于 \forall k\in [1,\frac{N!}{M!}], [(k-1)\times M!+1,k\times M!] 中与 M! 互质的数是相等的。
得出这个结论之后就简单了。答案就是 \frac{N!}{M!}\times \phi(M!).
我们知道 $\phi(n)=n\times\Pi_{p|n}(1-\frac{1}{p})$, 设 $phifac_i=\phi(i!)$,我们分两种情况讨论。
$n$ 为素数时,$phifac_i=n\times(1-\frac{1}{n})\times(n-1)!\times\Pi_{p|n!\&p\neq n}(1-\frac{1}{p})=(n-1)\times (n-1)!\times\Pi_{p|(n-1)!}(1-\frac{1}{p})=(n-1)\times phifac_{n-1}$.
$n$ 为合数时,
$phifac_i=n\times(n-1)!\times\Pi_{p|n!}(1-\frac{1}{p})=n\times phifac_{n-1}$.
这样我们就得到了阶乘的欧拉函数的递推式。
预处理出阶乘和欧拉函数,对于每个询问计算 $M!$ 的逆元即可。
但是题目中没有规定 $R\leq N$ 所以有可能会出现 $R|N!$,预处理阶乘和欧拉函数的时候要记得特判如果 $R|i$ 那么要把 $i$ 中的 $R$ 因子全部除掉再计算。
最后特判一下,如果 $\lfloor \frac{N}{R}\rfloor>\lfloor\frac{M}{R}\rfloor$ (即 $N!$ 中 $R$ 因子更多)则答案为 $0$.
```cpp
#include <cstdio>
using namespace std;
inline int read() {
int res=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {res=res*10+ch-'0'; ch=getchar();}
return res*f;
}
const int MAXN=10000005;
int T,r,n,m;
int ispr[MAXN];
int pri[MAXN];
int fct[MAXN];
int phi[MAXN];
inline void IsPrImE() {
int cnt=0;
ispr[0]=ispr[1]=1;
for(register int i=2;i<MAXN;++i) {
if(!ispr[i])
pri[++cnt]=i;
for(register int j=1;j<=cnt;++j) {
if(1ll*i*pri[j]>=MAXN) break;
ispr[i*pri[j]]=1;
if(!i%pri[j]) break;
}
}
}
inline long long qpow(int x,int p) {
long long res=1;
while(p) {
if(p&1) res=1ll*res*x%r;
p>>=1; x=1ll*x*x%r;
}
return res;
}
signed main() {
IsPrImE();
T=read(); r=read();
fct[0]=phi[0]=1;
for(register int i=1;i<MAXN;++i) {
int x=i,k=0;
while(x%r==0) x/=r,++k;
fct[i]=fct[i-1]*1ll*x%r;
x=i-1+ispr[i],k=0;
while(x%r==0) x/=r,++k;
phi[i]=phi[i-1]*1ll*x%r;
}
while(T--) {
n=read(); m=read();
long long ans=1ll*fct[n]*phi[m]%r*qpow(fct[m],r-2)%r;
if(n/r>m/r) ans=0;
printf("%lld\n",ans);
}
return 0;
}
```