BZOJ2186: [Sdoi2008]沙拉公主的困惑
Description
大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。
Input
第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模后面T行,每行一对整数N,M,见题目描述 m<=n
Output
共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值
Sample Input
1 11
4 2
Sample Output
1
数据范围:
对于100%的数据,1 < = N , M < = 10000000
Solution
因为保证\(m<=n\)
所以有\(m!|n!\)
因为\(m!\)内与\(m!\)互质的数为\(\phi(m!)\)
所以\(ans = \phi(m!)*\frac{n!}{m!}\)(相当于把\(n!\)分成多段,每段\(m!\))
\[\large
\begin{align*}
ans &= \phi(m!)*\frac{n!}{m!}\\
&=\frac{n!}{m!}*m!*\frac{\prod{p[i]-1}}{\prod{p[i]}}\\
&=n!*\frac{\prod{(p[i]-1)}}{\prod{p[i]}}
\end{align*}\\
\]
然后直接做就行了
#include <cstdio>
#include <iostream>
#include <bitset>
#define ll long long
#define inf 0x3f3f3f3f
#define il inline
int read()
{
char x;
while((x = getchar()) > '9' || x < '0') ;
int u = x - '0';
while((x = getchar()) <= '9' && x >= '0') u = (u << 3) + (u << 1) + x - '0';
return u;
}
int buf[105];
inline void write(int i) {
int p = 0;
if(i == 0) p++;
else while(i) {
buf[p++] = i % 10;
i /= 10;
}
for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);
}
using namespace std ;
#define N 10000010
int T, mod;
ll fac[N], inv[N];
ll ans[N];
bitset<N> pri;
// m <= n -> m!|n!
// 所以 n!中与m!互质的数的个数为 phi(m!)*(n!/m!)
int main() {
pri.set();
for(int i = 2; i < N; ++i) {
if(pri[i])
for(int j = i + i; j < N; j += i) pri[j] = 0;
}
T = read(); mod = read();
fac[0] = 1;
for(int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % mod;
inv[0] = inv[1] = 1;
for(int i = 2; i < N; ++i) {
if(i >= mod) break;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
ans[1] = 1;
for(int i = 2; i < N; i ++) {
if(pri[i]) {
ans[i] = ans[i - 1] * (i - 1) % mod;
ans[i] = ans[i] * inv[i % mod] % mod;
} else ans[i] = ans[i - 1];
}
int n, m;
while(T--) {
n = read(); m = read();
ll ANS = fac[n] * ans[m] % mod;
write(ANS), putchar('\n');
}
return 0;
}