2021暑期牛客多校1-G
G
首先,考虑如果不限制k的话,最优解如何构造。
显然,答案最大的情况就是前n大的数加,后面的数减。
那么如何说明总能构造出这种情况呢,
设 \(x_1,x_2\) 分别为 \(a,b\) 数列前 \(n\) 大的数字个数, \(y_1,y_2\) 分别为 \(a,b\) 数列前 \(n\) 小的个数。
显然下面的等式成立
解得
所以总能将 \(a\) 中前 \(n\) 大的数通过交换对应到 \(b\) 中前 \(n\) 小的数上,此时显然是最大的情况。
于是在不限制 \(k\) 的情况下,我们总能构造出最大的情况。
为了方便表述,将前 \(n\) 大记为正,前 \(n\) 小记为负,那么当答案最大时所有的正的对面一定是负。
此时如果一个数组有两个以上的正,则交换这两个正不会影响答案,所以对于 \(n>2\) 的情况,恰好 \(k\) 步等价于至多 \(k\) 步,对于 \(n=2\) 单独讨论。
下面以至多 \(k\) 步为前提讨论。
初始 \(a,b\) 数列,会出现正正,正负,负负三种配对情况。
-
正负不会和正负发生交换
显然交换后答案不会更优
-
正正不会和正负发生交换
考虑 \((2)\) ,有一对正正就说明有一对负负,而正正和负负交换后答案一定会变优,所以如果正正和正负发生了交换,那么交换过后的正正和负负交换会让答案变得更优。考虑两种交换最终对答案的贡献,会发现是相等的,终局都是正负*3,而第二种交换法浪费了次数,所以不会选择。
所以我们只需要考虑正正和负负交换即可,分别记为 \((s_1,s_2)\) , \((r_1, r_2)\) ,不难发现交换后贡献为 \(2*(min(s_1,s_2)-max(r_1,r_2))\)
所以我们将初始数组所有正正的配对按 \(min(s_1,s_2)\) 从大到小排,负负配对按 \(max(r_1,r_2)\) 从小往大排,取前 \(k\) 个相减乘2就是对答案的贡献。
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define cint const int&
#define Pi acos(-1)
const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n, k;
ll ans;
ll a[500500], b[500500];
bool via[500500], vib[500500];
vector<int> q1, e[2];
bool cmp(cint x, cint y) {
if(x < 0) return y < 0 ? b[-x] < b[-y] : b[-x] < a[y];
else return y < 0 ? a[x] < b[-y] : a[x] < a[y];
}
void debug() {
for(int i=1; i<=q1.size(); i++) {
cout << q1[i-1] << ' ';
}
cout << endl;
}
int main() {
cin >> n >> k;
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++) ans += abs(a[i] - b[i]);
if(n == 2) {
if(k & 1) cout << abs(a[1]-b[2])+abs(a[2]-b[1]) << endl;
else cout << ans << endl;
} else {
for(int i=1; i<=n; i++) q1.push_back(i);
for(int i=1; i<=n; i++) q1.push_back(-i);
sort(q1.begin(), q1.end(), cmp);
for(int i=0; i<n; i++) {
if(q1[i] > 0) via[q1[i]] = 1;
else vib[-q1[i]] = 1;
}
for(int i=1; i<=n; i++) {
if(via[i] && vib[i]) e[1].push_back(max(a[i], b[i]));
if(!via[i] && !vib[i]) e[0].push_back(min(a[i], b[i]));
}
sort(e[0].begin(), e[0].end());
sort(e[1].begin(), e[1].end());
int le = e[0].size();
for(int i=1; i<=min(k,le); i++) {
if(e[0][le-i] <= e[1][i-1]) break;
ans += 2*(e[0][le-i]-e[1][i-1]);
}
// debug();
cout << ans << endl;
}
return 0;
}