D. Counting Factorizations
The prime factorization of a positive integer $m$ is the unique way to write it as $\displaystyle m=p_1^{e_1}\cdot p_2^{e_2}\cdot \ldots \cdot p_k^{e_k}$, where $p_1, p_2, \ldots, p_k$ are prime numbers, $p_1 < p_2 < \ldots < p_k$ and $e_1, e_2, \ldots, e_k$ are positive integers.
For each positive integer $m$, $f(m)$ is defined as the multiset of all numbers in its prime factorization, that is $f(m)=\{p_1,e_1,p_2,e_2,\ldots,p_k,e_k\}$.
For example, $f(24)=\{2,3,3,1\}$, $f(5)=\{1,5\}$ and $f(1)=\{\}$.
You are given a list consisting of $2n$ integers $a_1, a_2, \ldots, a_{2n}$. Count how many positive integers $m$ satisfy that $f(m)=\{a_1, a_2, \ldots, a_{2n}\}$. Since this value may be large, print it modulo $998\,244\,353$.
Input
The first line contains one integer $n$ ($1\le n \le 2022$).
The second line contains $2n$ integers $a_1, a_2, \ldots, a_{2n}$ ($1\le a_i\le 10^6$) — the given list.
Output
Print one integer, the number of positive integers $m$ satisfying $f(m)=\{a_1, a_2, \ldots, a_{2n}\}$ modulo $998\,244\,353$.
Examples
input
2
1 3 2 3
output
2
input
2
2 2 3 5
output
5
input
1
1 4
output
0
Note
In the first sample, the two values of $m$ such that $f(m)=\{1,2,3,3\}$ are $m=24$ and $m=54$. Their prime factorizations are $24=2^3\cdot 3^1$ and $54=2^1\cdot 3^3$.
In the second sample, the five values of $m$ such that $f(m)=\{2,2,3,5\}$ are $200, 225, 288, 500$ and $972$.
In the third sample, there is no value of $m$ such that $f(m)=\{1,4\}$. Neither $1^4$ nor $4^1$ are prime factorizations because $1$ and $4$ are not primes.
解题思路
对于合法的 $f(m)=\{a_1, a_2, \ldots, a_{2n}\}$,$a_1, a_3, \ldots, a_{2n-1}$ 相当于 $m$ 质因数分解后的 $n$ 项底数,要求是 $n$ 个互不相同且递增的质数。剩下的 $a_2, a_4, \ldots, a_{2n}$ 则是对应项的指数,可以是任意数。因此在初始的 $2n$ 个元素中,如果互不相同的质数的数量小于 $n$,那么就不存在合法方案,答案为 $0$。
定义 $b_x$ 表示合数 $x$ 的出现次数,假设有 $s$ 个不同的合数。$c_x$ 表示质数 $x$ 的出现,假设有 $t$ 个不同的质数。当从 $t$ 个不同质数中选择了 $n$ 个作为底数,用 $c'_x$ 表示每个质数剩余的个数,其中如果选择了 $x$,则 $c'_x = c_x - 1$ 否则 $c'_x = c_x$。当确定了底数的选择后,那么合法的 $m$ 的数量就取决于剩余的 $n$ 个数能组成不同的指数方案,即$$\frac{n!}{b_1!\:\:b_2!\ldots b_s!\:\:c'_1!\:\:c'_2!\ldots c'_t!}$$
考虑所有可能的底数选择方案,那么最终答案就是各个方案所对应的上式的和。
注意到每个对于求和的每一项中,$\dfrac{n!}{b_1!\:\:b_2!\ldots b_s!}$ 都是一样的,因此只需求每一项的 $\dfrac{1}{c'_1!\:\:c'_2!\ldots c'_t!}$ 的和,最后乘上 $\dfrac{n!}{b_1!\:\:b_2!\ldots b_s!}$ 即可。
由于在选择底数的方案中,第 $i$ 个质数可选可不选,因此可以 01 背包来求所有方案的 $\dfrac{1}{c'_1!\:\:c'_2!\ldots c'_t!}$ 的总和。定义 $f(i,j)$ 表示从前 $i$ 个质数中选出了 $j$ 个的所有方案对应项的总和。假设第 $i$ 个质数是 $x$,状态转移方程为$$f(i,j) = f(i-1,j) \times \frac{1}{c_x!} + f(i-1,j-1) \times \frac{1}{(c_x - 1)!}$$
那么所有可能的底数选择方案对应的 $\dfrac{1}{c'_1!\:\:c'_2!\ldots c'_t!}$ 的总和就是 $f(t,n)$,最终答案就是 $\dfrac{n!}{b_1!\:\:b_2!\ldots b_s!} \times f(t,n)$。
由于涉及到出除法,可以先通过 $O(n \log {(\text{mod})})$ 的复杂度预处理出所有阶乘的乘法逆元,在状态转移时只需乘上阶乘的乘法逆元即可。
AC 代码如下,时间复杂度为 $O(n \log ({\text{mod}}) + n^2)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 4100, M = 1e6 + 10, mod = 998244353;
int a[N], c[M];
int primes[M], cnt;
bool vis[M];
int infact[N];
int f[N];
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) primes[cnt++] = i;
for (int j = 0; primes[j] * i <= n; j++) {
vis[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int qmi(int a, int k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
k >>= 1;
}
return ret;
}
int main() {
int n;
scanf("%d", &n);
infact[0] = 1;
for (int i = 1; i <= n << 1; i++) {
scanf("%d", a + i);
c[a[i]]++; // 统计每个元素的出现次数
infact[i] = 1ll * infact[i - 1] * qmi(i, mod - 2) % mod; // 计算阶乘i!的逆元
}
int ret = 1;
for (int i = 1; i <= n; i++) { // 计算n!
ret = 1ll * ret * i % mod;
}
get_prime(M - 1);
vis[1] = true;
vector<int> p;
for (int i = 1; i < M; i++) {
if (c[i]) {
if (vis[i]) ret = 1ll * ret * infact[c[i]] % mod; // 合数则直接乘上阶乘的逆元
else p.push_back(i); // 把质数找出来
}
}
f[0] = 1;
for (auto &x : p) {
for (int j = n; j >= 0; j--) { // 01背包的优化写法
f[j] = 1ll * f[j] * infact[c[x]] % mod;
f[j] = (f[j] + 1ll * f[j - 1] * infact[c[x] - 1]) % mod;
}
}
ret = 1ll * ret * f[n] % mod; // 把质数部分的逆元乘上
printf("%d", ret);
return 0;
}
参考资料
Codeforces Round 856 (Div. 2) Editorial:https://codeforces.com/blog/entry/113500
标签:prime,int,质数,Factorizations,2n,Counting,ldots,mod From: https://www.cnblogs.com/onlyblues/p/17830021.html