1. 程式人生 > 實用技巧 >[AtCoder Grand Contest 032] E Modulo Pairing(結論)

[AtCoder Grand Contest 032] E Modulo Pairing(結論)

Problem

題目地址

Solution

結論1

  • 最終答案一定存在一個分界點,滿足:

其中藍線表示 \(x+y<M\),紅線表示 \(x+y >= M\)

證明

對於任意 \(4\) 個點 \(a,b,c,d\),且滿足 \(a \le b \le c \le d \le M\)

  • 情況1: 假設其中任意兩個相加小於 \(M\),即對於 \(a,b,c,d\) 任意組合都滿足條件 \(x+y < M\)。那麼選擇 \((a,d),(b,c)\) 是最優的,即最大加最小,次大加次小。證明很容易:

  • 情況2: 假設其中任意兩個相加大於 \(M\),即對於 \(a,b,c,d\)

    任意組合都滿足條件 \(x+y >= M\)。那麼選擇 \((a,d),(b,c)\) 是最優的,即最大加最小,次大加次小。因為不妨把 \((x+y)\%M\) 寫成 \(x+y-M\),則證明和情況1是相同的。

  • 情況3: 紅線和藍線覆蓋或者交叉,那麼 選擇\((a,b),(c,d)\) 是最優的,而且一定滿足 \((a,b)\) 是藍線,\((c,d)\) 是紅線。證明如下:

綜上,如果紅線和藍線覆蓋或者交叉,那麼一定不是最終答案。反之,最終答案存在一個分界點,使得左部分全是藍線,右部分全是紅線。對於全是藍線的一塊,總是最大加最小,次大加次小最優,紅線的一塊同理。

結論2

  • 在分界點合法的前提下,分界點的位置越小,答案越優。
    (分界點合法即為右半部分最大加最小,次大加次小......都大於等於 \(M\))例如:

可以用類似的方法,分界點位置變小後,變化後藍線的一塊每個數的貢獻都與變化前有一一對應的變小,紅線的一塊同理,從而證明。

題解

根據 結論1結論2,我們可以二分合法且位置最小的分界點。時間複雜度 \(O(n \log n)\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 2e5+7;
int n,m;
int a[N];
bool check(int lim) {
	int len = lim << 1;
	for(int i=len+1;i<=2*n;++i) {
		if(a[i]+a[2*n-i+len+1] < m) return false;
	}
	return true;
}
int main()
{
	n = read(), m = read();
	for(int i=1;i<=2*n;++i) a[i] = read();
	sort(a+1, a+1+2*n);
	int l = 0, r = n, mid, ans = n;
	while(l <= r) {
		mid = (l + r) >> 1;
		if(check(mid)) {
			ans = mid; r = mid - 1;
		} else l = mid + 1;
	}
	int len = ans << 1, ansval = 0;
	for(int i=1;i<=len;++i) {
		ansval = max(ansval, a[i]+a[len-i+1]);
	}
	for(int i=len+1;i<=2*n;++i) {
		ansval = max(ansval, a[i]+a[2*n-i+len+1]-m);
	}
	printf("%d\n",ansval);
	//printf("%d\n",check(0));
    return 0;
}
/*
2 10
1 9 1 9

0
*/

Summary

(部分圖片摘自 Atcoder官方題解

多做題!