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 original segments all get marked no matter what, so their total length is fixed. You only control the extra segments created by pair operations.
For a paired pair , , the best new segment length is
So each pair wants one segment donating a small left endpoint and one segment donating a large right endpoint.
Think of assigning roles: exactly segments become left donors, and exactly become right donors. The extra score is
If segment is a right donor and segment is a left donor, but , swapping their roles is never worse. That means left donors should come earlier than right donors after sorting by .
After sorting by , try every split: choose the best left donors from the prefix, and the best right donors from the suffix. Prefix = smallest values, suffix = largest values. Heaps make this .
The original segments are all marked eventually. No exceptions, no clever loophole. So this part is fixed:
The only thing we can optimize is the sum of lengths of the new marked segments created when we pair two original segments.
For two segments and , we can choose one point from one segment and one point from the other. To maximize the length, we obviously take the smallest available left point and the largest available right point. Therefore the best extra length of this pair is
So the problem becomes: pair up segments, possibly leaving one unused if is odd, to maximize the sum of bounding interval lengths of the pairs.
Turn Pairs Into Roles
In every pair, one segment donates the left endpoint, and one segment donates the right endpoint.
If segment is a left donor, it contributes . If segment is a right donor, it contributes .
Let
We need exactly left donors and exactly right donors, all distinct, maximizing
Then the final answer is
The remaining question is: which segments should be left donors and which should be right donors?
The Key Swap Argument
Suppose segment is currently a right donor and segment is currently a left donor. Their contribution is
If we swap their roles, the contribution becomes
Swapping is at least as good when
Rearrange it:
So if has smaller than , then should not be the right donor while is the left donor. Swap them and the answer does not get worse.
That gives the structure of an optimal solution:
After sorting segments by , all left donors can be placed before all right donors.
That is the whole trick. The problem looks like pairing chaos, but it collapses into one sorted order.
Choosing Across a Split
Sort segments by .
For some split position :
We need choose exactly left donors and right donors.
For the prefix, to maximize , choose the smallest values.
For the suffix, to maximize , choose the largest values.
So we precompute:
pref[i] = best value of by choosing segments from the first segments;suf[i] = best value of by choosing segments from segments through .Then the best extra contribution is
This also handles odd naturally: one segment may sit between the chosen prefix donors and suffix donors, effectively unused for the extra pair score.
Maintaining the Best Prefix/Suffix Values
For pref, sweep left to right and keep the smallest values seen so far. Use a max-heap. If the heap gets bigger than , remove the largest .
For suf, sweep right to left and keep the largest values seen so far. Use a min-heap. If the heap gets bigger than , remove the smallest .
Each heap operation costs , so the whole solution is
per test case, with total . Fits easily.
#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 ll NEG = -(1LL << 60);
while (T--) {
int n;
cin >> n;
vector<pair<ll, ll>> seg(n);
ll base = 0;
for (auto &[l, r] : seg) {
cin >> l >> r;
base += r - l;
}
int m = n / 2;
sort(seg.begin(), seg.end(), [](const auto &a, const auto &b) {
return a.first + a.second < b.first + b.second;
});
vector<ll> pref(n + 1, NEG), suf(n + 1, NEG);
priority_queue<ll> lefts;
ll sumL = 0;
pref[0] = (m == 0 ? 0 : NEG);
for (int i = 0; i < n; i++) {
lefts.push(seg[i].first);
sumL += seg[i].first;
if ((int)lefts.size() > m) {
sumL -= lefts.top();
lefts.pop();
}
if ((int)lefts.size() == m) {
pref[i + 1] = -sumL;
}
}
priority_queue<ll, vector<ll>, greater<ll>> rights;
ll sumR = 0;
suf[n] = (m == 0 ? 0 : NEG);
for (int i = n - 1; i >= 0; i--) {
rights.push(seg[i].second);
sumR += seg[i].second;
if ((int)rights.size() > m) {
sumR -= rights.top();
rights.pop();
}
if ((int)rights.size() == m) {
suf[i] = sumR;
}
}
ll extra = 0;
for (int i = 0; i <= n; i++) {
if (pref[i] != NEG && suf[i] != NEG) {
extra = max(extra, pref[i] + suf[i]);
}
}
cout << base + extra << '\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 ll NEG = -(1LL << 60);
while (T--) {
int n;
cin >> n;
vector<pair<ll, ll>> seg(n);
ll base = 0;
for (auto &[l, r] : seg) {
cin >> l >> r;
base += r - l;
}
int m = n / 2;
sort(seg.begin(), seg.end(), [](const auto &a, const auto &b) {
return a.first + a.second < b.first + b.second;
});
vector<ll> pref(n + 1, NEG), suf(n + 1, NEG);
priority_queue<ll> lefts;
ll sumL = 0;
pref[0] = (m == 0 ? 0 : NEG);
for (int i = 0; i < n; i++) {
lefts.push(seg[i].first);
sumL += seg[i].first;
if ((int)lefts.size() > m) {
sumL -= lefts.top();
lefts.pop();
}
if ((int)lefts.size() == m) {
pref[i + 1] = -sumL;
}
}
priority_queue<ll, vector<ll>, greater<ll>> rights;
ll sumR = 0;
suf[n] = (m == 0 ? 0 : NEG);
for (int i = n - 1; i >= 0; i--) {
rights.push(seg[i].second);
sumR += seg[i].second;
if ((int)rights.size() > m) {
sumR -= rights.top();
rights.pop();
}
if ((int)rights.size() == m) {
suf[i] = sumR;
}
}
ll extra = 0;
for (int i = 0; i <= n; i++) {
if (pref[i] != NEG && suf[i] != NEG) {
extra = max(extra, pref[i] + suf[i]);
}
}
cout << base + extra << '\n';
}
return 0;
}