题目 | Matches
“范式杯”2023牛客暑期多校训练营1
H - Matches
https://ac.nowcoder.com/acm/contest/57355/H
题意
给定两个长度为 $n$ 的序列 $\{a\}_{i=1}^n$ 和 $\{b\}_{i=1}^n$, 现在可以选择其中一个序列交换其中的两个数字, 问经过至多一 次操作后最小的 $\sum_{i=1}^n\left|a_i-b_i\right|$。$1 \leq n \leq 2 \times 10^5, 0 \leq\left|a_i\right|,\left|b_i\right| \leq 10^{12}$ 。
题解
对于一对 $(a_i,b_i),(a_j,b_j)$,如果它们的大小关系相同,称为正序,若相反成为反序。另外,如果其中区间将另一个区间包含起来,称为包络,如果一个区间和另一个区间有交集,则称为相交。
如上图,对于一对 $(a_i,b_i),(a_j,b_j)$,交换后差值减小的情况只有反序包络与反序相交。因此考虑交换时,只需要考虑这两种情况。另外,差值减小的幅度 $\Delta$ 和两个区间相交的长度有关,具体来说,若两个反序区间相交的距离为 $\text{cross}$,那么 $\Delta=2\cdot\text{cross}$. 因此,在选择包络或相交的区间时,要选择相交长度最长的。
首先,我们将区间分成两类,$a<b$ 的区间的集合为 $s$,$a>b$ 的区间的集合为 $t$,另外 $a=b$ 的情况不会对最终答案产生影响,所以直接忽略。那我们的思路就变成,遍历其中一个集合的每一个区间,在另一个集合中找与其包络或相交的区间,下面代码遍历的 $t$.
那现在的问题就是,对于 $t$ 中每一个区间,如何快速的在 $s$ 中找到符合要求的最好的区间。
首先容易发现,如果 $s$ 中一个区间被另一个区间完全包含,那么我们选择大的区间,答案一定不会比小区间差。因为上文说到相交长度越长,减小幅度越大,$s$ 中大区间与当前 $t$ 中的区间的相交长度一定 $\geq$ $s$ 中被包含的小区间的相交长度。因此,应当将 $s$ 中的所有有包含关系的区间去除,保留最大区间。
经过上面的操作后,只要在 $s$ 中选择一个符合条件的区间,则必为对应条件的最优区间。由于 $s$ 中没有互相包含的区间,因此获得一个新性质,我们对 $s$ 的左端点进行排序后,$s$ 的右端点其实也有序了,这样的话左右端点都可以用二分找到了。
对于 $t$ 中的一个区间 $(x,y)$:
找 $s$ 中与它相交的
- 找左侧相交的:在 $s$ 中区间二分找左端点 $\leq x$ 的
- 找右侧相交的:在 $s$ 中区间二分找右端点 $\geq y$ 的
找 $s$ 中与它包络的
- 在 $s$ 中区间二分找左端点 $\leq x$ 的索引 $l$,找右端点 $\geq y$ 的索引 $r$,那么 $(l,r)$ 的区间就是被 $t$ 包络。
代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void solve()
{
int n;
cin >> n;
vector<pair<int, int>> a(n);
int sum = 0, cross = 0;
for (auto &p : a)
cin >> p.first;
for (auto &p : a)
cin >> p.second;
vector<pair<int, int>> s, t;
for (auto &[x, y] : a)
{
sum += abs(x - y);
if (x < y)
s.emplace_back(x, y);
else if (x > y)
t.emplace_back(y, x);
}
sort(s.begin(), s.end());
vector<int> sx, sy, len;
int rpos = INT64_MIN;
for (auto &[x, y] : s)
{
if (y <= rpos)
continue;
rpos = y;
sx.push_back(x);
sy.push_back(y);
len.push_back(y - x);
}
for (auto &[x, y] : t)
{
int tmp = 0;
int lpos = upper_bound(sx.begin(), sx.end(), x) - sx.begin();
int rpos = lower_bound(sy.begin(), sy.end(), y) - sy.begin();
if (lpos > 0)
tmp = max(tmp, min(y - x, sy[lpos - 1] - x));
if (rpos < sy.size())
tmp = max(tmp, min(y - x, y - sx[rpos]));
if (rpos > lpos)
tmp = max(tmp, *max_element(len.begin() + lpos, len.begin() + rpos));
cross = max(cross, tmp);
}
cout << sum - cross * 2 << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi