Description
大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。
R<=10^9+10
T<=10000
对于100%的数据,1 < = N , M < = 10000000 m<=n
Solution
一脸眼熟的题意
相当于求
我们知道欧拉函数是可以拆一下的,设 M!=pk11pk22...pknn M ! = p 1 k 1 p 2 k 2 . . . p n k n
显然M!的质因数都不大于M,线性筛一下就好了,打一个阶乘和逆元的表就ok
这里有一种线性递推求逆元的方法,mark一下
假设已知[1,i-1]模p意义下的逆元,现在要求i模p意义下的逆元
设
k=⌊pi⌋
k
=
⌊
p
i
⌋
,
j=p−ki
j
=
p
−
k
i
,那么有
ki+j≡0(modp)
k
i
+
j
≡
0
(
mod
p
)
两边同乘j、i的逆元有
kj−1+i−1≡0(modp)
k
j
−
1
+
i
−
1
≡
0
(
mod
p
)
随便移项
i−1≡−kj−1(modp)
i
−
1
≡
−
k
j
−
1
(
mod
p
)
写成递推柿子就是inv[i]=(p-p/i)*inv[p%i]%p
就是酱
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int N=10000005;
int prime[N/10],MOD;
bool not_prime[N+1];
LL fac[N+1],inv[N+1],ans[N+1];
void pre_work() {
rep(i,2,N) {
if (!not_prime[i]) prime[++prime[0]]=i;
for (int j=1;i*prime[j]<=N&&j<=prime[0];j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
fac[0]=1; rep(i,1,N) fac[i]=fac[i-1]*i%MOD;
inv[1]=1; rep(i,2,N) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
// rep(i,1,N) inv[i]=ksm(i,MOD-2)%MOD;
int now=1;
ans[1]=1;
rep(i,2,N) {
ans[i]=ans[i-1];
while (prime[now]<=i&&now<=prime[0]) {
ans[i]=ans[i]*(prime[now]-1)%MOD*inv[prime[now]]%MOD;
now++;
}
}
}
void solve(int n,int m) {
printf("%lld\n", ans[m]*fac[n]%MOD);
}
int main(void) {
int T; scanf("%d%d",&T,&MOD);
pre_work();
while (T--) {
int n,m; scanf("%d%d",&n,&m);
solve(n,m);
}
return 0;
}