题解 SP22412 【DIVFACT3 - Divisors of factorial (hard)】

Warriors_Cat

2020-12-24 13:09:08

Solution

$upd\ on \ 2021.2.13$ 修正了个小笔误。 --- [题面传送门](https://www.luogu.com.cn/problem/SP22412)。 > 题意:$T$ 组询问,每次给定 $n, m$,求 $n!$ 因子个数对 $m$ 取模的值,$n, m \le 10^8$。 算是个较为好想的题? --- ### $Solution:$ 我们令: $$n! = \prod_{i=1}^kp_i^{\alpha_i}$$ 显然因子个数 $d(n!)$ 就为: $$\prod_{i=1}^k(\alpha_i + 1)$$ 对 $n!$ 这个数来说,显然有: $$\alpha_k = \sum_{i=1}^{\left\lfloor\log_{p_k}n\right\rfloor}\left\lfloor\dfrac{n}{{p_k}^i}\right\rfloor$$ 发现对于 $p_k > \sqrt{n}$ 的所有 $p_k$,其贡献只能有 $\left\lfloor\dfrac{n}{p_k}\right\rfloor$,可以用数论分块来搞。对于一个块 $(L, R)$,其贡献为 $\left\lfloor\dfrac{n}{L}\right\rfloor^{f_R - f_{L-1}}$,其中 $f_i$ 表示 $1$ 到 $i$ 中质数的个数。 对于 $p_k \le \sqrt{n}$ 的所有 $p_k$ 仍然考虑暴力。再套上线性筛 $10^8$ 以内的 $f$ 函数即可。 over,时间复杂度为 $O(N + T\sqrt{N})$。 --- ### $Code:$ ```cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <vector> #include <queue> #include <map> #include <set> using namespace std; #define int long long #define ull unsigned long long #define fi first #define se second #define dingyi int mid = l + r >> 1, ls = p << 1, rs = p << 1 | 1 #define y0 y_csyakioi_0 #define y1 y_csyakioi_1 #define rep(i, x, y) for(int i = x; i <= y; ++i) #define per(i, x, y) for(int i = x; i >= y; --i) #define repn(i, x, y, z) for(int i = x; i <= y; i += z) #define pern(i, x, y, z) for(int i = x; i >= y; i -= z) inline int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ x = x * 10 + (ch ^ 48); ch = getchar(); } return x * f; } const int N = 100000010; int n, m, pri[N], len, f[N]; inline int fpow(int x, int p, int mod){ int ans = 1; for(; p; p >>= 1, x = x * x % mod) if(p & 1) ans = ans * x % mod; return ans; } inline void sieve(int n){ f[0] = f[1] = 1; rep(i, 2, n){ if(!f[i]) pri[++len] = i; rep(j, 1, len){ if(i * pri[j] > n) break; f[i * pri[j]] = 1; if(i % pri[j] == 0) break; } } rep(i, 1, n) f[i] = f[i - 1] + 1 - f[i]; } inline int fac(int x, int y){ return x == 0 ? 0 : fac(x / y, y) + x / y; } inline void work(){ n = read(); m = read(); int ans = 1, k = sqrt(n); for(int i = 1; pri[i] <= k; ++i) ans = ans * (fac(n, pri[i]) + 1) % m; for(int i = k + 1, j; i <= n; i = j + 1){ j = n / (n / i); ans = ans * fpow(n / i + 1, f[j] - f[i - 1], m) % m; } printf("%lld\n", ans); } signed main(){ int qwq = read(); sieve(N - 10); while(qwq--) work(); return 0; } ```