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.
Forget elevator capacity. A valid solution is just an ordering of all starting and destination floors, with each before its .
Open the absolute values as signs. Every non-final floor appears in two distance terms, while the final floor appears once. There are exactly plus signs and minus signs, plus the constant from starting at floor .
Sort all floors as . If the final floor is fixed, the best sign assignment is greedy: put plus signs on the largest available copies and minus signs on the smallest copies.
This greedy sign pattern is basically an alternating walk between the low half and the high half . If some starting floor is in the high half, the precedence constraints can be made to fit that alternation.
The only nasty leftover case is when all starts are in the low half and all finishes are in the high half. Then the walk must start low, so only the boundary floors can be the exceptions. Check those few formulas directly.
A false instinct here is to simulate elevator decisions or DP over boarded people. That is dead on arrival: the set of passengers can be enormous. The elevator capacity is mostly a distraction. What matters is the order in which the special floors are visited.
Sign view
Let the visited floors be . The only validity rule is that every appears before its matching . The time is
.
Since all floors are above , the first term is . Every later absolute value contributes one endpoint with a plus sign and one endpoint with a minus sign. So, ignoring the constant :
So the problem becomes: choose the final destination and assign signs as profitably as possible. Weird? Yes. Useful? Very.
Best signs for a fixed final floor
Sort all floors:
.
Call the low half and the high half. Let
, .
If we temporarily ignore precedence, the best sign assignment is obvious: put plus signs on the biggest copies and minus signs on the smallest copies.
Suppose the final floor is .
If , one low copy is missing because appears once instead of twice. Then all high copies get plus signs, all low copies get minus signs, and has coefficient instead of :
.
If , one high copy is missing. There are only high copies, so one plus sign must go to the largest low floor, . Thus has coefficient , the final has coefficient , and the formula is
.
These are upper bounds for any real route, because no route can beat the best possible sign assignment.
When a high-half start exists
If at least one starting floor lies in the high half, those upper bounds are actually reachable.
The sign pattern wants an alternating route between high and low floors. High floors become local maxima with coefficient , low floors become local minima with coefficient , and the final floor gets only one sign. Starting from a high-half starting floor gives the walk the correct first move.
What about precedence? If the next desired floor is legal, take it. If all desired floors on the other side are illegal destinations, then their matching starts are still on the current side, so board one of those starts instead and continue. This local repair keeps the alternation alive. The elevator is evil, but not magic.
Therefore, in this case we just try every destination rank and take the corresponding formula above.
The all-up case
Now assume there is no high-half start. Since there are exactly starts total, this means all starts are in the low half and all destinations are in the high half.
Now the route must start at a low floor and finish at a high floor. The best alternating route would like to start at the largest low floor and finish at the smallest high floor , giving
.
This is valid if and belong to different people. Start at , eventually board the person whose destination is , and finish there.
If and are the same person, that perfect boundary choice breaks: after starting at , the only immediately deliverable high floor is , which we wanted to save for the end. So we must compromise. There are only three optimal-looking compromises:
.
.
.
Take the maximum of these three. For , there is no boundary juggling: the only route is .
Implementation
Sort the floors, keeping whether each floor is a start or finish and its person id. Compute and with prefix sums.
Then:
Sorting dominates, so the complexity is per test case, with memory. The sum of is , so this cruises.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Node {
ll x;
bool start;
int id;
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<Node> a;
a.reserve(2 * n);
for (int i = 0; i < n; i++) {
ll s, f;
cin >> s >> f;
a.push_back({s, true, i});
a.push_back({f, false, i});
}
sort(a.begin(), a.end(), [](const Node& p, const Node& q) {
return p.x < q.x;
});
vector<ll> pref(2 * n + 1, 0);
for (int i = 0; i < 2 * n; i++) pref[i + 1] = pref[i] + a[i].x;
ll low = pref[n];
ll high = pref[2 * n] - pref[n];
bool hasHighStart = false;
for (int i = n; i < 2 * n; i++) {
hasHighStart |= a[i].start;
}
const ll NEG = -(1LL << 62);
ll ans = NEG;
if (hasHighStart) {
for (int k = 0; k < 2 * n; k++) {
if (a[k].start) continue;
if (k < n) {
ans = max(ans, -1 - 2 * low + a[k].x + 2 * high);
} else {
ans = max(ans, -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[k].x);
}
}
} else {
if (n == 1) {
ans = a[1].x - 1;
} else if (a[n - 1].id != a[n].id) {
ans = -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[n].x;
} else {
ans = max(ans, -1 - 2 * low + 2 * a[n - 2].x + 2 * high - a[n].x);
ans = max(ans, -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[n + 1].x);
ans = max(ans, -1 - 2 * low + 4 * a[n - 1].x + 2 * high - 3 * a[n].x);
}
}
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Node {
ll x;
bool start;
int id;
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<Node> a;
a.reserve(2 * n);
for (int i = 0; i < n; i++) {
ll s, f;
cin >> s >> f;
a.push_back({s, true, i});
a.push_back({f, false, i});
}
sort(a.begin(), a.end(), [](const Node& p, const Node& q) {
return p.x < q.x;
});
vector<ll> pref(2 * n + 1, 0);
for (int i = 0; i < 2 * n; i++) pref[i + 1] = pref[i] + a[i].x;
ll low = pref[n];
ll high = pref[2 * n] - pref[n];
bool hasHighStart = false;
for (int i = n; i < 2 * n; i++) {
hasHighStart |= a[i].start;
}
const ll NEG = -(1LL << 62);
ll ans = NEG;
if (hasHighStart) {
for (int k = 0; k < 2 * n; k++) {
if (a[k].start) continue;
if (k < n) {
ans = max(ans, -1 - 2 * low + a[k].x + 2 * high);
} else {
ans = max(ans, -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[k].x);
}
}
} else {
if (n == 1) {
ans = a[1].x - 1;
} else if (a[n - 1].id != a[n].id) {
ans = -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[n].x;
} else {
ans = max(ans, -1 - 2 * low + 2 * a[n - 2].x + 2 * high - a[n].x);
ans = max(ans, -1 - 2 * low + 2 * a[n - 1].x + 2 * high - a[n + 1].x);
ans = max(ans, -1 - 2 * low + 4 * a[n - 1].x + 2 * high - 3 * a[n].x);
}
}
cout << ans << '\n';
}
}