D. A Cruel Segment's Thesis
D. A Cruel Segment's Thesis
You are given $n$ segments on a number line. The $i$-th segment is represented as $[l_i, r_i]$. Initially, all the segments are unmarked.
You perform the following operation repeatedly until there are no unmarked segments left:
- In the $k$-th operation, if there are at least two unmarked segments, choose any two unmarked segments $[l_i, r_i]$ and $[l_j, r_j]$, mark both of them, and add a new marked segment $[x_k, y_k]$ satisfying the following conditions:
- $l_i \leq x_k \leq r_i$,
- $l_j \leq y_k \leq r_j$,
- $x_k \leq y_k$.
- If there is exactly one unmarked segment remaining, mark it.
Your task is to determine the maximum possible sum of lengths of all the marked segments at the end of the process. Note that the length of a segment $([l,r])$ is $r-l$.
Input
Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 10^4$). The description of the test cases follows.
The first line of each test case contains a single integer $n$ ($1 \leq n \leq 2 \cdot 10^5$) — the number of segments.
Each of the next $n$ lines contains two integers $l_i$ and $r_i$ ($1 \leq l_i \leq r_i \leq 10^9$) — the $i$-th segment.
It is guaranteed that the sum of $n$ across all test cases does not exceed $2 \cdot 10^5$.
Output
For each test case, print a single integer — the maximum possible total length of all marked segments at the end of the process.
Example
Input
4
2
1 1000000000
1 1000000000
3
1 10
2 15
3 9
5
1 11
2 7
15 20
1 3
11 15
1
1000000000 1000000000
Output
2999999997
42
59
0
Note
In the first test case, we choose the given two segments and make the new segment $[1, 10^9]$.
In the second test case, we choose the segments $[1, 10]$ and $[2, 15]$ and make the new segment $[1,15]$. Now, $[3, 9]$ is the only segment that is left unmarked, and it will be marked in the next step.
解题思路
赛时看错题了,以为新生成的的区间不会有标记,浪费了好多时间。
每个原始区间的长度都会被累加,而新生成的区间又会被标记(意味着不会被用作生成新的区间),因此我们只需考虑如何生成尽可能长的区间。假设有区间 $[l_i, r_i]$ 和 $[l_j, r_j]$,为了得到最大长度,显然应该分别选择这两个区间的端点来构成新区间。即新区间要么是 $[l_i, r_j]$,要么是 $[l_j, r_i]$(可能会出现左端点大于右端点的情况)。由此可见,每个原始区间要么贡献左端点,要么贡献右端点。当然,这是在原始区间数量为偶数的情况下。如果原始区间的数量为奇数,那么会有一个区间既不贡献左端点,也不贡献右端点。不过我们可以统一成偶数的情况来处理,对于奇数的情况,可以通过枚举来确定不产生贡献的区间,剩下的部分则可视为偶数的情况来处理。
考虑 $n$ 为偶数的情况,现在需要选择 $\tfrac{n}{2}$ 个区间贡献左端点,另外 $\tfrac{n}{2}$ 个区间贡献右端点。如何分配贡献,才能使右端点的和减去左端点的和最大呢?这是一个经典问题,可以参考 E2. Game with Marbles (Hard Version),博客有对这类问题进行总结。具体做法就是先让每个区间都贡献右端点,此时总和为 $s = \sum\limits_{i=1}^{n}{r_i}$,然后再贪心地让其中的 $\tfrac{n}{2}$ 个区间从贡献右端点变成贡献左端点。具体来说,当区间 $[l_i, r_i]$ 从贡献右端点变成贡献左端点时,总和的变化为 $s \gets s - r_i - l_i$,即区间让 $s$ 减少了 $l_i + r_i$。由于我们希望总和 $s$ 最大,自然就会让关于 $l_i + r_i$ 最小的 $\tfrac{n}{2}$ 个区间的贡献从右端点变成左端点。
所以我们可以根据 $l_i + r_i$ 对区间从小到大排序,那么 $\sum\limits_{i=1}^{n}{r_i} - \sum\limits_{i=1}^{\tfrac{n}{2}}{l_i+r_i}$ 就是新生成区间的最大长度。再加上原始区间的长度总和 $\sum\limits_{i=1}^{n}{r_i - l_i}$ 就是要求的答案。
对于 $n$ 为奇数的情况,我们只需枚举每个区间作为不产生贡献的区间,然后用上述方法求剩余区间的答案,再取所有情况的答案取最大值即可。为了避免每次枚举都重新计算剩余区间的答案,我们可以按 $l_i + r_i$ 对区间从小到大排序后再枚举。记前 $\left\lfloor\tfrac{n}{2}\right\rfloor$ 个区间关于 $l_i + r_i$ 的和为 $s$,当枚举的是前 $\left\lfloor\tfrac{n}{2}\right\rfloor$ 个区间时,那么删除第 $i$ 个区间的贡献后,第 $\left\lfloor\tfrac{n}{2}\right\rfloor + 1$ 小的区间的贡献就会从右端点变成左端点,此时有 $s \gets s - (l_i + r_i) + \left(l_{\left\lfloor\tfrac{n}{2}\right\rfloor + 1} + r_{\left\lfloor\tfrac{n}{2}\right\rfloor + 1}\right)$,此时剩余区间的答案就是 $\sum\limits_{i=1}^{n}{r_i - l_i} + \left(\sum\limits_{i=1}^{n}{r_i}\right) - s - r_i$。如果枚举的不是前 $\left\lfloor\tfrac{n}{2}\right\rfloor$ 个区间,那么删除第 $i$ 个区间的贡献不会影响剩余区间的贡献(还是前 $\left\lfloor\tfrac{n}{2}\right\rfloor$ 个区间贡献左端点),此时 $s$ 不变,剩余区间的答案与之前的公式相同。
AC 代码如下,时间复杂度为 $O(n \log{n})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int l[N], r[N], p[N];
void solve() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> l[i] >> r[i];
}
iota(p, p + n, 0);
sort(p, p + n, [&](int i, int j) {
return l[i] + r[i] < l[j] + r[j];
});
LL s = 0;
for (int i = 0; i < n >> 1; i++) {
s += l[p[i]] + r[p[i]];
}
LL mn = s;
if (n & 1) {
mn = 1e18;
for (int i = 0; i < n; i++) {
if (i < n >> 1) mn = min(mn, s - l[p[i]] - r[p[i]] + l[p[n >> 1]] + r[p[n >> 1]] + r[p[i]]);
else mn = min(mn, s + r[p[i]]);
}
}
LL ret = -mn;
for (int i = 0; i < n; i++) {
ret += 2 * r[i] - l[i];
}
cout << ret << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 1049 (Div. 2) Editorial:https://codeforces.com/blog/entry/146218
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/19089642