AGC032E Modulo Pairing

AGC032E

有个\(2*n\)的数组\(a_i\),你要给其中的数两两配对,使得两个数之和模\(M\)的最大值最小。

\(n\le 10^5\)

\(m\le 10^9\)


简化题意:对于两个数\(x\)\(y\),若\(x+y<M\),则答案和\(x+y\)\(\max\);若\(x+y\geq M\),则答案和\(x+y-m\)\(\max\)

先考虑一个简化的问题:如果要两两配对,使得它们的和的最大值最小,怎么做?

一眼就可以看出做法:小的和大的依次配对。两眼可以想出证明:考虑如果存在四个数\(a\le b\le c\le d\),假如存在\(\max(a+d,b+c)>\max(a+c,b+d)\)。如果\(\max(a+d,b+c)=a+d\),由于\(a+d \le b+d\)矛盾;如果\(\max(a+d,b+c)=b+c\),由于\(b+c\le b+d\)矛盾。所以\((a,d)(b,c)\)配对总是比\((a,c)(b,d)\)优。

其实也可以发现,如果是要让最小值最大,还是小的和大的依次配对最优。

所以如果要求和在一个区间内,最小配最大的方式,可以尽可能地将所有和压在这个区间内。

然后是一个结论:取\(x+y<M\)\(x+y\ge M\)的数,分别在数组的左右两边。

这里搞一个性质:设有\(a\le b \le c\le d\),且\(a+c<M\and b+d\ge M\),那么将\((a,c)(b,d)\)替换成\((a,b)(c,d)\)会更优。

证明:即证\(\max(a+c,b+d-M)\ge \max(a+b,c+d-M)\)。显然\(a+b\le a+c\),并且因\(d< M\le M+a\),所以\(c+d-M\le a+c\)

于是如果有“相交”的情况,就可以调整成包含。

那么一直调整下去,最后它们就分别在数组的两边了。

枚举这个分界点,然后两边以最小配最大原则来搞。这样是\(O(n^2)\)的,但是注意到,如果合法,两边的最大值是随着这个分界点递增而递增的;分界点太大则左半边不合法,分界点太小则右半边不合法。于是可以二分解决。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
int n,m;
int a[N];
int ans;
int calcf(int l,int r){
	int f=0;
	for (int i=1;l+i-1<r-i+1;++i){
		if (a[l+i-1]+a[r-i+1]>=m)
			return -1;
		f=max(f,a[l+i-1]+a[r-i+1]);
	}
	return f;
}
int calcg(int l,int r){
	int g=0;
	for (int i=1;l+i-1<r-i+1;++i){
		if (a[l+i-1]+a[r-i+1]<m)
			return -1;
		g=max(g,a[l+i-1]+a[r-i+1]);
	}
	return g;
}
void work(){
	ans=m-1;
	int l=0,r=n/2;
	while (l<=r){
		int mid=l+r>>1;
		int f=calcf(1,mid*2),g=calcg(mid*2+1,n);
		if (f==-1)
			r=mid-1;
		else if (g==-1)
			l=mid+1;
		else{
			ans=min(ans,max(f,g-m));
			r=mid-1;
		}
	}
}
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	n*=2;
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	work();
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-09-27 21:13  jz_597  阅读(114)  评论(0编辑  收藏  举报