F - Subsequence LCM
F - Subsequence LCM
Problem Statement
You are given a sequence of positive integers of length and a positive integer . Find the number, modulo , of non-empty and not necessarily contiguous subsequences of such that the least common multiple (LCM) of the elements in the subsequence is . Two subsequences are distinguished if they are taken from different positions in the sequence, even if they coincide as sequences. Also, the LCM of a sequence with a single element is that element itself.
Constraints
- All input values are integers.
Input
The input is given from Standard Input in the following format:
Output
Print the answer.
Sample Input 1
4 6
2 3 4 6
Sample Output 1
5
The subsequences of whose elements have an LCM of are ; there are five of them.
Sample Input 2
5 349
1 1 1 1 349
Sample Output 2
16
Note that even if some subsequences coincide as sequences, they are distinguished if they are taken from different positions.
Sample Input 3
16 720720
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Sample Output 3
2688
解题思路
一些技巧。要对 求 或 ,先质因数分解,使得每个 都有共同的质因子 (不存在则用 表示):
那么就有:
由于子序列的 等于确定的 ,所以对 进行质因数分解,有 ,容易知道在 内一个数最多有 个不同的质因子,因此这里的 最大为 。同时这样的子序列中每个元素必然满足 ,因此一开始先把 的元素删掉,那么剩下的元素必定都能表示成 ,且 。
根据上面提到求 的技巧,如果每个 都有 ,那么该子序列的 才能为 。也就是说选出来的子序列中,在每一个 上都至少存在一个元素该项的次幂为 。为此我们可以用 位的二进制进行状态压缩,对于每一个元素,如果该元素质因数分解后 项的次幂恰好是 ,那么置第 位为 ,否则置为 。
所以现在的问题变成了有多少种选择方案,使得选出来元素对应的二进制状态的按位或等于 (即 个位均为 )。可以用 dp 解决,定义 表示从前 个元素中选出状态按位或等于 的方案数量。考虑从 可以转移到哪些状态,状态转移方程为 (不选择第 个元素);(选择第 个元素, 的状态表示为 )。
这样做的时间复杂度是 ,会超时。注意到最多有 个元素,而所有元素的状态最多有 种,因此我们可以统计状态 的数量表示为 。那么 表示从前 个状态中选出按位或等于 的方案数量。状态转移方程为 ;。
我的代码实现是通过类似 01 背包的方式优化掉第一维,预处理出 。直接用 的复杂度质因数分解 。还有要记得特判 的情况。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 1 << 13, mod = 998244353;
int cnt[M];
int f[M];
int p[N];
int main() {
int n;
LL m;
scanf("%d %lld", &n, &m);
vector<LL> fs;
LL t = m;
for (LL i = 2; i * i <= t; i++) {
if (t % i == 0) {
LL p = 1;
while (t % i == 0) {
p *= i;
t /= i;
}
fs.push_back(p);
}
}
if (t > 1) fs.push_back(t);
for (int i = 1; i <= n; i++) {
LL x;
scanf("%lld", &x);
if (m % x) continue;
int t = 0;
for (int j = 0; j < fs.size(); j++) {
if (x % fs[j] == 0) t |= 1 << j;
}
cnt[t]++;
}
p[0] = 1;
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] * 2 % mod;
}
f[0] = 1;
for (int i = 0; i < 1 << fs.size(); i++) {
if (!cnt[i]) continue;
for (int j = (1 << fs.size()) - 1; j >= 0; j--) {
f[j | i] = (f[j | i] + (p[cnt[i]] - 1ll) * f[j]) % mod;
}
}
printf("%d", f[(1 << fs.size()) - 1] - (m == 1));
return 0;
}
参考资料
Editorial - AtCoder Beginner Contest 349:https://atcoder.jp/contests/abc349/editorial/9801
ABC349F题 Subsequence LCM讲解(DP暴力做法 or 高维前缀和):https://www.bilibili.com/video/BV1jr42157Fa/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18144463
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效