1. 程式人生 > 其它 >Atcoder Grand Contest 032 E - Modulo Pairing(亂搞+二分)

Atcoder Grand Contest 032 E - Modulo Pairing(亂搞+二分)

Atcoder 題面傳送門 & 洛谷題面傳送門

神仙調整+亂搞題。

首先某些人(including me)一看到最大值最小就二分答案,事實上二分答案對這題正解沒有任何啟發。

首先將 \(a_i\) 從小到大排序。我們考慮將分配的點對看作一條條線,對於 \(a_x+a_y<M\) 的點對 \((x,y)\) 我們在 \(x,y\) 之間連一條藍線,對於 \(a_x+a_y\ge M\) 的點對我們在 \(x,y\) 之間連一條紅線。

先拋結論,再給證明:如在最優分配方式中,我們的連線方式肯定是長這樣的:

證明:使用調整法,證明上述命題,等價於證明對於以下 \(7\) 種情況,左邊的情況都可以被調整為右邊的情況且答案不會更劣(這裡借用了粉兔題解中的圖):

我們考慮一一對其進行證明,為了表述方便我們統一假設從左到右四個點分別為 \(a_p,a_q,a_r,a_s\),則顯然 \(a_p\le a_q\le a_r\le a_s\)

  1. 對於左邊第一個的情況,左邊的最大值為 \(\max(a_p+a_q,a_r+a_s)=a_r+a_s\),右邊的最大值為 \(\max(a_p+a_s,a_q+a_r)\),而由於 \(a_p+a_s\le a_r+a_s,a_q+a_r\le a_r+a_s\),故右邊答案不會比左邊更劣。
  2. 對於右邊第一個的情況,左邊的最大值為 \(\max(a_p+a_r,a_q+a_s-M)\),而由於 \(a_s-M<0\)
    ,故 \(a_q+a_s-M<a_q<a_p+a_r\),故左邊的最大值實際上是 \(a_p+a_r\),右邊的最大值為 \(\max(a_p+a_q,a_r+a_s-M)\),而顯然 \(a_p+a_q\le a_p+a_r,a_r+a_s-M<a_r\le a_p+a_r\),故右邊答案不會比左邊更劣,同時又因為 \(a_p+a_q\le a_p+a_r<M,a_r+a_s\ge a_q+a_s\ge M\),故 \(a_p,a_q\) 之間連的依舊是藍線,\(a_r,a_s\) 之間連的依舊是紅線。
  3. 對於左邊第二個的情況,左邊的最大值為 \(a_q+a_s\),右邊的最大值為 \(\max(a_p+a_s,a_q+a_r)\)
    ,而 \(a_p+a_s\le a_q+a_s,a_q+a_r\le a_q+a_s\),故右邊答案不會比左邊更劣。
  4. 對於右邊第二個的情況,左邊的最大值為 \(\max(a_p+a_s,a_q+a_r-M)=a_p+a_s\),右邊的最大值為 \(\max(a_p+a_q,a_r+a_s-M)\),又由於 \(a_p+a_q\le a_p+a_s,a_r+a_s-M<a_r\le a_p+a_s\),故右邊答案不會比左邊更劣。
  5. 對於左邊第三、四個的情況,證明方法同左邊第一、二個,只不過需要整體減個 \(M\)
  6. 對於右邊第三個的情況,左邊最大值為 \(\max(a_q+a_r,a_p+a_s-M)=a_q+a_r\),右邊最大值為 \(\max(a_p+a_q,a_r+a_s-M)\),又由於 \(a_p+a_q\le a_q+a_r,a_r+a_s-M<a_r\le a_q+a_r\),故右邊答案不會比左邊更劣。

綜上,只要出現線相交或者不同顏色的線出現包含關係的情況,都可以被調整,證畢。

接下來考慮怎樣求答案,暴力列舉分割點顯然是不可行的,不過注意到對於兩個不同且均合法的分割點 \(p\)\(p'\),如果 \(p<p'\),那麼以 \(p\) 為分割點的每條線的權值都小於以 \(p'\) 為分割點的每條線的權值,因此我們肯定希望分割點越靠左越好,而如果我們分割點太左了(yyq:政治學得很好嘛),那就會出現右側有的線不是紅線的情況,因此我們可以二分找出合法的且最靠左的分割點 \(p\),然後求出答案即可。

時間複雜度線性對數。

namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	template<typename T> void print(T x,char c){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);putc(c);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
using namespace fastio;
const int MAXN=1e5;
int n,m,a[MAXN*2+5];
int add(int x,int y){return (x+y<m)?(x+y):(x+y-m);}
bool check(int mid){bool flg=1;for(int i=(mid<<1)+1;i<=n<<1;i++) flg&=(a[i]+a[(n<<1)+(mid<<1)+1-i]>=m);return flg;}
int main(){
	read(n);read(m);
	for(int i=1;i<=n<<1;i++) read(a[i]);
	sort(a+1,a+(n<<1)+1);int l=0,r=n,p=-1;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) p=mid,r=mid-1;
		else l=mid+1;
	} int mx=0;
//	printf("%d\n",p);
	for(int i=1;i<=p<<1;i++) chkmax(mx,add(a[i],a[(p<<1)+1-i]));
	for(int i=(p<<1)+1;i<=n<<1;i++) chkmax(mx,add(a[i],a[(n<<1)+(p<<1)+1-i]));
	printf("%d\n",mx);
	return 0;
}