AGC032E
有个(2*n)的数组(a_i),你要给其中的数两两配对,使得两个数之和模(M)的最大值最小。
(nle 10^5)
(mle 10^9)
简化题意:对于两个数(x)和(y),若(x+y<M),则答案和(x+y)取(max);若(x+ygeq M),则答案和(x+y-m)取(max)。
先考虑一个简化的问题:如果要两两配对,使得它们的和的最大值最小,怎么做?
一眼就可以看出做法:小的和大的依次配对。两眼可以想出证明:考虑如果存在四个数(ale ble cle 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+cle b+d)矛盾。所以((a,d)(b,c))配对总是比((a,c)(b,d))优。
其实也可以发现,如果是要让最小值最大,还是小的和大的依次配对最优。
所以如果要求和在一个区间内,最小配最大的方式,可以尽可能地将所有和压在这个区间内。
然后是一个结论:取(x+y<M)和(x+yge M)的数,分别在数组的左右两边。
这里搞一个性质:设有(ale b le cle d),且(a+c<Mand b+dge M),那么将((a,c)(b,d))替换成((a,b)(c,d))会更优。
证明:即证(max(a+c,b+d-M)ge max(a+b,c+d-M))。显然(a+ble a+c),并且因(d< Mle M+a),所以(c+d-Mle 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
",ans);
return 0;
}