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.
Do not try to simulate where every key is. Once you are standing at the current frontier room, the only thing that matters for the future is how many keys you deliberately brought there.
Let be the earliest time you can be in the current room with spare keys available for future doors. If the current room initially has a key, you may either ignore it or add it, so update .
Moving spare keys across one already-open door costs seconds for : take one right, then for each remaining key go left and come back. For , it costs just second to walk through.
For door , either wait until it opens by time and keep the same number of keys, or spend one key immediately and reduce the count by one. That gives the two transitions: wait/open and key/open.
You only need about key-count states. Carrying future keys across the frontier has already forced at least seconds of shuttling, while waiting for all doors and walking gives an answer at most .
The main trap is thinking the one-key carrying limit makes the state tiny. Nope. You can only carry one key at a time, but you can pile many keys in a room, so the real cost is the walking needed to shuttle that pile forward.
Process doors from left to right. Before door , suppose we are in room . Define as the minimum time when we can be in room with exactly spare keys intentionally available at this frontier for future use. A key may be lying in the room or currently carried; we do not need to distinguish those, because the next movement cost is the same.
If , room has a new key. We may ignore it, or add it to the frontier pile for free:
Now handle door .
First, if we wait until the door opens automatically, then we can cross no earlier than time . If we want to preserve all spare keys on the right side, the crossing/shuttling cost is:
So:
Second, if , we can spend one key to open the door immediately. The first key is consumed while crossing, and the other keys must be shuttled over if we want them available for later. This still costs seconds total:
Why is correct? For an open door and , carry one key right in second. For each of the remaining keys, go left and come back right, costing more seconds each. Total . For a closed door opened by a key, the first carried key is consumed, but the timing is identical.
After processing door , the answer for room is . Positive key counts mean we spent extra time hauling keys for later doors. For the current target alone, unused future keys are dead weight; leaving them behind can only help. The DP still keeps all because later answers are independent and may need those slower-but-better-stocked states.
Now the important optimization: we do not keep all possible . Let
A dumb but valid strategy is to wait in room until all doors have opened, then walk right, so every answer is at most .
Also, any state that transports spare keys across the frontier has time at least . This follows by induction: before using a room's fresh key, having keys means at least roughly time has already been paid, and moving those keys across the next door adds , giving . So if , that state can never improve an answer; the wait-and-walk strategy already beats it. Carrying that many keys is just cardio with extra steps.
Therefore keep only states. Since , this is under about states per door.
Edge cases are naturally handled: with no keys only the automatic-opening transition works; if , crossing can happen during second and arrival time is ; if a key is in the current room, adding it costs no time.
The complexity is per test case, where , and the memory usage is .
#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<int> a(n);
int mx = 0;
for (int &x : a) {
cin >> x;
mx = max(mx, x);
}
string s;
cin >> s;
int M = mx + n;
int lim = 0;
while ((lim + 1) * (lim + 1) <= M) ++lim;
lim += 3;
vector<int> f(lim + 1, INF), nf(lim + 1, INF);
f[0] = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == '1') {
for (int j = lim - 1; j >= 0; --j) {
f[j + 1] = min(f[j + 1], f[j]);
}
}
fill(nf.begin(), nf.end(), INF);
for (int j = 0; j <= lim; ++j) {
if (f[j] == INF) continue;
int move_cost = max(1, 2 * j - 1);
nf[j] = min(nf[j], max(f[j], a[i]) + move_cost);
if (j > 0) {
nf[j - 1] = min(nf[j - 1], f[j] + 2 * j - 1);
}
}
cout << nf[0] << (i + 1 == n ? '\n' : ' ');
f.swap(nf);
}
}
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<int> a(n);
int mx = 0;
for (int &x : a) {
cin >> x;
mx = max(mx, x);
}
string s;
cin >> s;
int M = mx + n;
int lim = 0;
while ((lim + 1) * (lim + 1) <= M) ++lim;
lim += 3;
vector<int> f(lim + 1, INF), nf(lim + 1, INF);
f[0] = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == '1') {
for (int j = lim - 1; j >= 0; --j) {
f[j + 1] = min(f[j + 1], f[j]);
}
}
fill(nf.begin(), nf.end(), INF);
for (int j = 0; j <= lim; ++j) {
if (f[j] == INF) continue;
int move_cost = max(1, 2 * j - 1);
nf[j] = min(nf[j], max(f[j], a[i]) + move_cost);
if (j > 0) {
nf[j - 1] = min(nf[j - 1], f[j] + 2 * j - 1);
}
}
cout << nf[0] << (i + 1 == n ? '\n' : ' ');
f.swap(nf);
}
}
return 0;
}