D. Counting Factorizations
D. Counting Factorizations
The prime factorization of a positive integer is the unique way to write it as , where are prime numbers, and are positive integers.
For each positive integer , is defined as the multiset of all numbers in its prime factorization, that is .
For example, , and .
You are given a list consisting of integers . Count how many positive integers satisfy that . Since this value may be large, print it modulo .
Input
The first line contains one integer ().
The second line contains integers () — the given list.
Output
Print one integer, the number of positive integers satisfying modulo .
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 such that are and . Their prime factorizations are and .
In the second sample, the five values of such that are and .
In the third sample, there is no value of such that . Neither nor are prime factorizations because and are not primes.
解题思路
对于合法的 , 相当于 质因数分解后的 项底数,要求是 个互不相同且递增的质数。剩下的 则是对应项的指数,可以是任意数。因此在初始的 个元素中,如果互不相同的质数的数量小于 ,那么就不存在合法方案,答案为 。
定义 表示合数 的出现次数,假设有 个不同的合数。 表示质数 的出现,假设有 个不同的质数。当从 个不同质数中选择了 个作为底数,用 表示每个质数剩余的个数,其中如果选择了 ,则 否则 。当确定了底数的选择后,那么合法的 的数量就取决于剩余的 个数能组成不同的指数方案,即
考虑所有可能的底数选择方案,那么最终答案就是各个方案所对应的上式的和。
注意到每个对于求和的每一项中, 都是一样的,因此只需求每一项的 的和,最后乘上 即可。
由于在选择底数的方案中,第 个质数可选可不选,因此可以 01 背包来求所有方案的 的总和。定义 表示从前 个质数中选出了 个的所有方案对应项的总和。假设第 个质数是 ,状态转移方程为
那么所有可能的底数选择方案对应的 的总和就是 ,最终答案就是 。
由于涉及到出除法,可以先通过 的复杂度预处理出所有阶乘的乘法逆元,在状态转移时只需乘上阶乘的乘法逆元即可。
AC 代码如下,时间复杂度为 :
#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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17830021.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-11-13 摆放棋子
2022-11-13 D. Yet Another Problem