AGC032E
有个 2 ∗ n 2*n 2∗n的数组 a i a_i ai,你要给其中的数两两配对,使得两个数之和模 M M M的最大值最小。
n ≤ 1 0 5 n\le 10^5 n≤105
m ≤ 1 0 9 m\le 10^9 m≤109
简化题意:对于两个数 x x x和 y y y,若 x + y < M x+y<M x+y<M,则答案和 x + y x+y x+y取 max \max max;若 x + y ≥ M x+y\geq M x+y≥M,则答案和 x + y − m x+y-m x+y−m取 max \max max。
先考虑一个简化的问题:如果要两两配对,使得它们的和的最大值最小,怎么做?
一眼就可以看出做法:小的和大的依次配对。两眼可以想出证明:考虑如果存在四个数 a ≤ b ≤ c ≤ d a\le b\le c\le d a≤b≤c≤d,假如存在 max ( a + d , b + c ) > max ( a + c , b + d ) \max(a+d,b+c)>\max(a+c,b+d) max(a+d,b+c)>max(a+c,b+d)。如果 max ( a + d , b + c ) = a + d \max(a+d,b+c)=a+d max(a+d,b+c)=a+d,由于 a + d ≤ b + d a+d \le b+d a+d≤b+d矛盾;如果 max ( a + d , b + c ) = b + c \max(a+d,b+c)=b+c max(a+d,b+c)=b+c,由于 b + c ≤ b + d b+c\le b+d b+c≤b+d矛盾。所以 ( a , d ) ( b , c ) (a,d)(b,c) (a,d)(b,c)配对总是比 ( a , c ) ( b , d ) (a,c)(b,d) (a,c)(b,d)优。
其实也可以发现,如果是要让最小值最大,还是小的和大的依次配对最优。
所以如果要求和在一个区间内,最小配最大的方式,可以尽可能地将所有和压在这个区间内。
然后是一个结论:取 x + y < M x+y<M x+y<M和 x + y ≥ M x+y\ge M x+y≥M的数,分别在数组的左右两边。
这里搞一个性质:设有 a ≤ b ≤ c ≤ d a\le b \le c\le d a≤b≤c≤d,且KaTeX parse error: Undefined control sequence: \and at position 6: a+c<M\̲a̲n̲d̲ ̲b+d\ge M,那么将 ( a , c ) ( b , d ) (a,c)(b,d) (a,c)(b,d)替换成 ( a , b ) ( c , d ) (a,b)(c,d) (a,b)(c,d)会更优。
证明:即证 max ( a + c , b + d − M ) ≥ max ( a + b , c + d − M ) \max(a+c,b+d-M)\ge \max(a+b,c+d-M) max(a+c,b+d−M)≥max(a+b,c+d−M)。显然 a + b ≤ a + c a+b\le a+c a+b≤a+c,并且因 d < M ≤ M + a d< M\le M+a d<M≤M+a,所以 c + d − M ≤ a + c c+d-M\le a+c c+d−M≤a+c。
于是如果有“相交”的情况,就可以调整成包含。
那么一直调整下去,最后它们就分别在数组的两边了。
枚举这个分界点,然后两边以最小配最大原则来搞。这样是 O ( n 2 ) O(n^2) O(n2)的,但是注意到,如果合法,两边的最大值是随着这个分界点递增而递增的;分界点太大则左半边不合法,分界点太小则右半边不合法。于是可以二分解决。
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;
}

被折叠的 条评论
为什么被折叠?



