1. 程式人生 > 其它 >2021暑期牛客多校1-G

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;
}