Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Only the values that appear in the array matter. Since you may reuse an element, one copy of value is just as useful as ten copies of value .
Think of building products from . If you already know how to make using selected elements, then for every available value , you can make using elements, as long as .
This is a shortest-path problem on numbers : there is an edge for each available . All edges cost .
You do not need Dijkstra or BFS. Every edge goes from a smaller number to a larger number, so processing in increasing order is already a valid topological order.
Set only as an internal starting point, ignore multiplier in transitions, and after the DP answer query separately: it is exactly when the array contains a , otherwise .
The repeated-use part is the whole problem. Once a value appears in the array, it can be used any number of times, so duplicates do not matter at all. We only care about the set
For every product , let be the minimum number of selected elements needed to obtain product . Internally, use
as the empty product. That does not mean the answer for is ; the statement requires selecting at least one element. This is just a clean DP starting point.
For any available value , if product is reachable, then product is reachable with one more selected element:
We only apply this when , because answers are needed only for through .
A nice way to view this is as a graph. The vertices are numbers . For every available multiplier , add an edge
with cost . We need shortest distances from .
Normally “shortest paths” might make you reach for BFS, but here every edge strictly increases the number, since . So the graph is a DAG, and the natural order
is already topological. Process from small to large. When is known, relax all transitions using available multipliers.
To keep it efficient, store all distinct available values greater than . For each reachable , stop the multiplier loop once . Since the values are sorted, this is easy.
The total number of useful relaxations is bounded by
Now handle the annoying little edge case: product . The only way to get product while selecting at least one element is to select value . Multipliers greater than can never help. Therefore:
For every , if stayed infinite, output ; otherwise output .
Correctness follows from the transition matching exactly one extra selected element. Any valid product representation
with each and corresponds to a path
of length , so the DP can find it. Conversely, every DP transition multiplies by an actually available array value, so every reached state is a real selectable product. Since states are processed in increasing order and every transition goes to a larger state, all ways to reach have already been considered before is used to update larger products.
Complexity per test case is
with memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
const int INF = 1e9;
while (t--) {
int n;
cin >> n;
vector<char> has(n + 1, false);
for (int i = 0; i < n; i++) {
int x;
cin >> x;
has[x] = true;
}
vector<int> vals;
vals.reserve(n);
for (int x = 2; x <= n; x++) {
if (has[x]) vals.push_back(x);
}
vector<int> dp(n + 1, INF);
dp[1] = 0;
for (int v = 1; v <= n; v++) {
if (dp[v] == INF) continue;
int limit = n / v;
for (int x : vals) {
if (x > limit) break;
dp[v * x] = min(dp[v * x], dp[v] + 1);
}
}
for (int i = 1; i <= n; i++) {
int ans;
if (i == 1) ans = has[1] ? 1 : -1;
else ans = (dp[i] == INF ? -1 : dp[i]);
if (i > 1) cout << ' ';
cout << ans;
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
const int INF = 1e9;
while (t--) {
int n;
cin >> n;
vector<char> has(n + 1, false);
for (int i = 0; i < n; i++) {
int x;
cin >> x;
has[x] = true;
}
vector<int> vals;
vals.reserve(n);
for (int x = 2; x <= n; x++) {
if (has[x]) vals.push_back(x);
}
vector<int> dp(n + 1, INF);
dp[1] = 0;
for (int v = 1; v <= n; v++) {
if (dp[v] == INF) continue;
int limit = n / v;
for (int x : vals) {
if (x > limit) break;
dp[v * x] = min(dp[v * x], dp[v] + 1);
}
}
for (int i = 1; i <= n; i++) {
int ans;
if (i == 1) ans = has[1] ? 1 : -1;
else ans = (dp[i] == INF ? -1 : dp[i]);
if (i > 1) cout << ' ';
cout << ans;
}
cout << '\n';
}
return 0;
}