2021暑期牛客多校1-G

G

首先,考虑如果不限制k的话,最优解如何构造。

显然,答案最大的情况就是前n大的数加,后面的数减。

那么如何说明总能构造出这种情况呢,

\(x_1,x_2\) 分别为 \(a,b\) 数列前 \(n\) 大的数字个数, \(y_1,y_2\) 分别为 \(a,b\) 数列前 \(n\) 小的个数。

显然下面的等式成立

\[\begin{cases} x_1+y_1=x_2+y_2 \\ x_1+x_2=y_1+y_2 \end{cases} \]

解得

\[\begin{cases} x_1=y_2 \\ y_1=x_2 \end{cases} \]

所以总能将 \(a\) 中前 \(n\) 大的数通过交换对应到 \(b\) 中前 \(n\) 小的数上,此时显然是最大的情况。

于是在不限制 \(k\) 的情况下,我们总能构造出最大的情况。

为了方便表述,将前 \(n\) 大记为正,前 \(n\) 小记为负,那么当答案最大时所有的正的对面一定是负。

此时如果一个数组有两个以上的正,则交换这两个正不会影响答案,所以对于 \(n>2\) 的情况,恰好 \(k\) 步等价于至多 \(k\) 步,对于 \(n=2\) 单独讨论。

下面以至多 \(k\) 步为前提讨论。

初始 \(a,b\) 数列,会出现正正,正负,负负三种配对情况。

  1. 正负不会和正负发生交换

    显然交换后答案不会更优

  2. 正正不会和正负发生交换

    考虑 \((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;
}
posted @ 2021-07-20 12:36  ullio  阅读(52)  评论(0编辑  收藏  举报