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;
}