H.Matches
题意
有两个数列
a
,
b
a,b
a,b,现在可以选择其中一个数列,交换其中两个数字,最多可以操作一次,问操作后最小的
∑
i
=
1
n
=
∣
a
[
i
]
−
b
[
i
]
∣
\sum_{i=1}^n=|a[i]-b[i]|
∑i=1n=∣a[i]−b[i]∣
数据范围:
1
<
=
n
<
=
2
5
,
0
<
=
∣
a
[
i
]
∣
,
∣
b
[
i
]
∣
<
=
1
0
9
1<=n<=2^5,0<=|a[i]|,|b[i]|<=10^9
1<=n<=25,0<=∣a[i]∣,∣b[i]∣<=109
解题思路
做这题要先知道正序和反序
定义:
对于一对
(
a
[
x
]
,
b
[
x
]
)
(a[x],b[x])
(a[x],b[x])和
(
a
[
y
]
,
b
[
y
]
)
(a[y],b[y])
(a[y],b[y]),不妨设a[x]< b[x],若a[y]> b[y]则称他们成反序关系,若a[y]< b[y]则为正序。
相交:部分重合,但各自有独立部分
包络:一部分完全在另一部分范围,完全包络
不交:完全没有重合部分
交换后的改变量为
∣
a
[
x
]
−
b
[
x
]
∣
+
∣
a
[
y
]
−
b
[
y
]
∣
−
∣
a
[
x
]
−
b
[
y
]
∣
−
∣
a
[
y
]
−
b
[
x
]
∣
|a[x]-b[x]|+|a[y]-b[y]|-|a[x]-b[y]|-|a[y]-b[x]|
∣a[x]−b[x]∣+∣a[y]−b[y]∣−∣a[x]−b[y]∣−∣a[y]−b[x]∣
要求sum最小,即该变量要最大
用图片表示交换a[x],a[y]后的关系
情况1:正序相交——>正序包络
改变量为0
情况2:反序包络——>反序不交
改变量>0
情况3:反序相交——>反序不交
改变量>0
改变量的值就是重合部分的两倍
综上:只需要在反序之间找到重合部分的最大值
把a[x]< b[x]为A序列,a[y]> b[y]为B序列
分为两个循环情况找重合部分
情况1
A序列在前,B序列在后
b[y] a[y]
——————————————
——————————————
a[x] b[x]
情况2
B序列在前,A序列在后
a[x] b[x]
——————————————
——————————————
b[y] a[y]
分别找到最大的重合部分
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pII pair<int,int>
const int N=1e6+6;
int n;
int sA, sB;
vector<pII>A, B;
int a[N], b[N];
signed main()
{
cin >> n;
int res = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++)
{
res += abs(a[i] - b[i]);
if (a[i] < b[i]) A.push_back(make_pair(a[i], b[i]));
if (b[i] < a[i]) B.push_back(make_pair(b[i], a[i]));
}
sA = A.size();
sB = B.size();
sort(A.begin(), A.end());
sort(B.begin(), B.end());
int mx = -2e9;
int sum = res;
//cout << sum << '\n';
for (int i = 0; i < sA; i++)
{
for (int j = 0; j < sB && B[j].first <= A[i].first; j++)//要有相交部分
{
mx = max(mx, B[j].second);//相交的部分最大化
}
if (mx >= A[i].first)
res = min(res, sum - 2 * (min(mx, A[i].second) - A[i].first));
}
mx = -2e9;
cout<<res <<' ';
for (int i = 0; i < sB; i++)
{
for (int j = 0; j < sA && A[j].first <= B[i].first; j++)//要有相交部分
{
mx = max(mx, A[j].second);//相交的部分最大化
}
if (mx >= B[i].first)
res = min(res, sum - 2 * (min(mx, B[i].second) - B[i].first));//减掉重合部分的两倍
}
cout << res << '\n';
}