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.
The condition talks about every subarray, but GCD is not that dramatic. Try asking whether preserving all adjacent-pair GCDs already forces every longer subarray GCD to stay fixed.
For index , let the required adjacent GCDs be and . Any final value at must be divisible by
with the obvious one-neighbor version at the ends.
If that minimum value is strictly smaller than and still not above , take it immediately. It gives one operation and only removes factors, so it cannot create new bad GCD collisions. Free point. Don't overthink the free point.
The only annoying indices are those where the minimum legal value is exactly . Such an index can only be changed upward, and any useful upward change can be replaced by multiplying by a single prime .
Build candidates and, only when , candidates for the first few primes. Then do path DP:
Transition from candidate at to at iff
The first primes are enough because neighboring values are at most , while their product is already bigger than that.
The statement asks us to preserve the GCD of every subarray of length at least . That sounds awful, but for any interval ,
So preserving all adjacent values
is necessary and sufficient. The monster was just a path DP wearing a fake mustache.
For each index define
Every valid final value at must be divisible by . Also , because both adjacent GCDs divide .
If and , then set and count one operation. This is always optimal: if some larger multiple works with the neighbors, replacing it by still keeps each adjacent GCD exactly the same, because it only removes factors. Free operation, take it.
Let
The base candidate has cost if , otherwise cost .
If , index is already changed, and multiplying it further gives no extra score. It can only make GCD collisions easier, which is useless.
If , then changing index means choosing some value , where and . If a composite works, then one prime factor also works, because divides . So we only need prime multipliers.
We also do not need all primes. Fix the neighbors of . A prime is bad only if it appears in the extra part of a neighbor beyond the required adjacent GCD. The product of the two relevant neighbor values is at most
But the product of the first primes, , is greater than . Therefore the neighbors cannot forbid all of them. If is smaller than , then every possible prime multiplier is already among these first primes anyway. That is the whole trick; miss it and the problem becomes a swamp.
For each index , build candidates:
Now run DP along the path:
A transition from value at to value at is legal iff
So
Each position has at most candidates, hence the complexity is
with DP memory. This easily fits for .
Reference checked: Codeforces Round 1089 editorial.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
const vector<ll> primes = {
2, 3, 5, 7, 11,
13, 17, 19, 23, 29,
31, 37, 41, 43, 47,
53, 59, 61, 67, 71
};
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> a(n), b(n);
for (ll &x : a) cin >> x;
for (ll &x : b) cin >> x;
vector<ll> g(n - 1);
for (int i = 0; i + 1 < n; ++i) {
g[i] = gcd(a[i], a[i + 1]);
}
auto make_candidates = [&](int i) {
vector<pair<ll, int>> res;
res.reserve(primes.size() + 1);
ll need;
if (i == 0) {
need = g[0];
} else if (i == n - 1) {
need = g[n - 2];
} else {
ll x = g[i - 1], y = g[i];
need = x / gcd(x, y) * y;
}
ll base = (need <= b[i] ? need : a[i]);
res.push_back({base, base != a[i]});
if (base == a[i]) {
for (ll p : primes) {
if (a[i] <= b[i] / p) {
res.push_back({a[i] * p, 1});
}
}
}
return res;
};
const int NEG = -1'000'000'000;
vector<pair<ll, int>> prev = make_candidates(0);
vector<int> dp(prev.size(), NEG);
for (int j = 0; j < (int)prev.size(); ++j) {
dp[j] = prev[j].second;
}
for (int i = 1; i < n; ++i) {
vector<pair<ll, int>> cur = make_candidates(i);
vector<int> ndp(cur.size(), NEG);
for (int j = 0; j < (int)cur.size(); ++j) {
for (int k = 0; k < (int)prev.size(); ++k) {
if (dp[k] == NEG) continue;
if (gcd(cur[j].first, prev[k].first) == g[i - 1]) {
ndp[j] = max(ndp[j], dp[k] + cur[j].second);
}
}
}
prev.swap(cur);
dp.swap(ndp);
}
cout << *max_element(dp.begin(), dp.end()) << '\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();
const vector<ll> primes = {
2, 3, 5, 7, 11,
13, 17, 19, 23, 29,
31, 37, 41, 43, 47,
53, 59, 61, 67, 71
};
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> a(n), b(n);
for (ll &x : a) cin >> x;
for (ll &x : b) cin >> x;
vector<ll> g(n - 1);
for (int i = 0; i + 1 < n; ++i) {
g[i] = gcd(a[i], a[i + 1]);
}
auto make_candidates = [&](int i) {
vector<pair<ll, int>> res;
res.reserve(primes.size() + 1);
ll need;
if (i == 0) {
need = g[0];
} else if (i == n - 1) {
need = g[n - 2];
} else {
ll x = g[i - 1], y = g[i];
need = x / gcd(x, y) * y;
}
ll base = (need <= b[i] ? need : a[i]);
res.push_back({base, base != a[i]});
if (base == a[i]) {
for (ll p : primes) {
if (a[i] <= b[i] / p) {
res.push_back({a[i] * p, 1});
}
}
}
return res;
};
const int NEG = -1'000'000'000;
vector<pair<ll, int>> prev = make_candidates(0);
vector<int> dp(prev.size(), NEG);
for (int j = 0; j < (int)prev.size(); ++j) {
dp[j] = prev[j].second;
}
for (int i = 1; i < n; ++i) {
vector<pair<ll, int>> cur = make_candidates(i);
vector<int> ndp(cur.size(), NEG);
for (int j = 0; j < (int)cur.size(); ++j) {
for (int k = 0; k < (int)prev.size(); ++k) {
if (dp[k] == NEG) continue;
if (gcd(cur[j].first, prev[k].first) == g[i - 1]) {
ndp[j] = max(ndp[j], dp[k] + cur[j].second);
}
}
}
prev.swap(cur);
dp.swap(ndp);
}
cout << *max_element(dp.begin(), dp.end()) << '\n';
}
return 0;
}