题目链接:D. Counting Factorizations
题解(动态规划):
根据题意,一种合法的方案应该是从原数组中选取n个不同的质数作为底数,其他数作为质数;答案即为所有的方案数。(PS:含有重复元素的全排列公式:,其中a、b、c...为每种重复元素的个数)
-
比较显然的是如果指数的种类小于n则答案为0
-
假设非质数有A种,每种个数为q(a),质数有B种,每种个数为p(b)
现在我们选取前n种质数作为底数,那么该种选择的方案数就是余下元素的全排列(含有重复元素)
带入全排列公式就是:
其中是一个定值可以先算出来
后面部分与其他情况通分之后底数就是,也可以先算出来
而分子就是所有情况的乘积之和
-
这里我们可以把问题简化一下:有B个数,任选n个数相乘,求所有情况的乘积之和。这个问题可以用动态规划的方法求:
-
:前i个数中选j个数相乘所有情况的乘积之和
代码示例:
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <vector>
#include <stack>
#include <list>
#include <bitset>
#include <utility>
#include <deque>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <set>
#include <string>
#include <sstream>
#include <functional>
#include <cstdlib>
#include <string.h>
#include <bitset>
#include <string_view>
#include <thread>
#include <future>
#define ll long long
#define ull unsigned long long
#define srt short
template<class _Tp>
struct GT {
constexpr bool operator()(const _Tp& a, const _Tp& b) const { return a > b; }
};
template<class _Tp>
struct LS {
constexpr bool operator()(const _Tp& a, const _Tp& b) const { return a < b; }
};
const ll mod = 998244353;
const int N = 4100, M = 1000010;
int n;
// 线性筛
std::vector<int> primes;
bool tag[M];
void Init() {
tag[0] = tag[1] = true;
primes.reserve(8e4);
for (int i = 2; i < M; i++) {
if (!tag[i]) primes.push_back(i);
for (auto j : primes) {
int tmp = i * j;
if (tmp >= M) break;
tag[tmp] = true;
if (i % j == 0) break;
}
}
fact[0] = 1;
for (int i = 1; i < N; i++) (fact[i] = i * fact[i - 1]) %= mod;
}
// 预处理阶乘
ll fact[N];
// 记录原数组中各数字个数
int cnt[M];
// 原数组各质数的个数
int p[N], tot;
// 快速幂
inline ll Pow(ll base, ll power) {
ll res = 1;
while (power) {
if (power & 1) (res *= base) %= mod;
power >>= 1;
(base *= base) %= mod;
}
return res;
}
// 费马小定理求逆元
inline ll Inv(ll x) { return Pow(x, mod - 2); }
ll f[N][N];
int main() {
Init();
scanf("%d", &n);
int x;
for (int i = 1; i <= n << 1; i++) {
scanf("%d", &x);
cnt[x]++;
}
ll ans = 1;
for (int i = 1; i < M; i++) {
if (cnt[i]) {
// 求定值
(ans *= fact[cnt[i]]) %= mod;
if (!tag[i]) p[++tot] = cnt[i];
}
}
// 质数个数小于n的情况
if (tot < n) {
puts("0");
return 0;
}
ans = Inv(ans) * fact[n] % mod;
for (int i = 1; i <= tot; i++) {
f[i][1] = f[i - 1][1] + p[i];
int k = std::min(i, n);
for (int j = 2; j <= k; j++) {
(f[i][j] = f[i - 1][j] + p[i] * f[i - 1][j - 1]) %= mod;
}
}
(ans *= f[tot][n]) %= mod;
printf("%lld", ans);
return 0;
}