「JOI Open 2020」黑白點


傳送門

題意

環上有 \(n\) 個黑點和 \(n\) 個白點。現在要將黑點、白點通過 \(n\) 條線段兩兩匹配,問最多幾對線段相交。

\(n\le 2\times 10^5\)

分析

首先,對於依次排列的 B1B2W1W2,最優方案一定不包含 B1-W2 B2-W1,因為可以調整成 B1-W1 B2-W2

有推論:設黑、白點序列分別為 \(b_1,b_2,\cdots,b_n\)\(w_1,w_2,\cdots,w_n\),最優方案一定是 \(b\) 的某個循環移位和 \(w\) 在所有下標相同的位置匹配。

聚焦於一條線段,可推得:與之不相交的線段條數為 \(|l-n|\)\(l\) 為線段長度。

|a-b|=|n-l|

因此,把所有白點移動到正對面,問題轉化為:將黑白點匹配,最小化匹配距離之和。

數軸上的問題是容易的(每段單位長度的貢獻為其左側黑點數與白點數之差的絕對值)。考慮把環復制無窮遍,相當於可以將極左側的黑、白點數之差賦為任意整數。因此取中位數最優。

代碼

提交記錄

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),_=(b);i<=_;++i)
#define per(i,a,b) for(int i=(a),_=(b);i>=_;--i)
#define pb push_back
#define IL inline
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
int n, m, a[N];
char s[N];
int main() {
    scanf("%d%s", &n, s + 1), m = n * 2;

    rep(i, 1, m)if (s[i] == 'B')
        ++a[i];

    rep(i, 1, m)if (s[i] == 'W')
        --a[i <= n ? i + n : i - n];

    rep(i, 1, m)a[i] += a[i - 1];
    sort(a + 1, a + m + 1);
    ll ans = ll(n) * (n - 1);
    rep(i, 1, n)ans -= a[m + 1 - i] - a[i];
    printf("%lld\n", ans >> 1);
    exit(0);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM