662 字
3 分钟
CodeForces 2193E: E. Product Queries
题目链接
题目大意
题目描述
给定一个长度为 的数组 ()。你需要依次回答 个问题。
对于第 个问题( 从 到 ):
- 目标:从数组 中选择最少数量的元素,使得这些元素的乘积恰好等于 。
- 规则:数组中的同一个元素可以被重复选择多次。
- 限制:至少需要选择 1 个元素。
输出要求
对于每一个 ,输出一个整数:
- 如果能凑出乘积 ,输出所需的最少元素个数。
- 如果无法凑出,输出 。
数据范围
- (测试用例数量)
- 所有测试用例中 的总和不超过 。
样例解释摘要
假设 ,数组 。
- 目标 4:可以选择两个 ( 和 或者重复选 ),乘积 ,数量为 2。
- 目标 6:可以直接选一个 (),数量为 1。虽然也可以选 ,但那需要 2 个数,不是最少。
- 目标 5:数组里没有 5,也无法通过组合得到 5,输出 -1。
解题思路
核心算法:动态规划 (DP) + 调和级数复杂度优化。
-
预处理
- 使用一个标记数组
has[x]记录数字 是否出现在输入数组 中。
- 使用一个标记数组
-
状态定义
- 令
dp[i]表示乘积恰好为 所需的最少元素个数。 - 初始化:所有
dp[i] = INF。对于输入中存在的每个数 ,令dp[x] = 1。
- 令
-
状态转移
- 从小到大枚举当前乘积 (从 到 )。
- 如果
dp[i]可达(即 INF),则尝试向后转移:- 枚举倍数:遍历 。
- 计算因子 。
- 判定与更新:如果因子 存在于原数组中(即
has[v]为真),则尝试更新:
-
复杂度分析
- 核心在于双层循环:外层枚举 ,内层枚举 的倍数。
- 总运算次数约为 。
- 时间复杂度:,在 时完全可以接受。
代码实现
#include <iostream>#include <bits/stdc++.h>using namespace std;const int N=3e5+10;const int INF = 1e9;int T, n;int a[N], dp[N], has[N];void solve() { scanf("%d", &n); memset(has, 0, sizeof has); memset(dp, 0x3f, sizeof dp); for(int i=1;i<=n;i++) { scanf("%d", &a[i]); has[a[i]]=1; dp[a[i]]=1; } for(int i=1;i<=n;i++) { if(dp[i]==0x3f3f3f3f) continue; for(int j=2*i;j<=n;j+=i){ int v=j/i; if(has[v]) dp[j]=min(dp[j], dp[i]+1); } } for(int i=1;i<=n;i++) { if(dp[i]==0x3f3f3f3f) printf("-1 "); else printf("%d ", dp[i]); } printf("\n");}
int main() { int t; scanf("%d", &t); while (t--) { solve(); } return 0;}“千里之行,始于足下。”
CodeForces 2193E: E. Product Queries
https://shenyize.com/solutions/codeforces/2193e/