agc032E Modulo Pairing
- 将 2 n 2n 2n个数两两配对,使得每一对的和在模 m m m意义下最大值最小。
- n ≤ 1 e 5 n\le1e5 n≤1e5
Solution
-
首先二分答案 m i d mid mid。
-
然后考虑配对是可以贪心的,如果我们将 2 n 2n 2n个数先分成两个集合 A , B A,B A,B,集合 A A A内的和是 [ 0 , m i d ] [0,mid] [0,mid], B B B是 [ m , m + m i d ] [m,m+mid] [m,m+mid],那么这两个集合内部肯定是首尾配对。
-
再考虑 A , B A,B A,B如果相交或包含的话,都可以找到一个更优的代替,即不存在 A , B A,B A,B相交或包含。
-
因此前一部分 A A A,后面的部分是 B B B。
-
直接枚举后一半的中间点,判断一下可不可行,找到一个最大的 B B B即可。
-
O ( n l o g n ) O(n\ log \ n) O(n log n)
-
在考虑贪心的时候不要直接排除一些情况,有可能定量分析的时候可以发现其中的性质。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 200005
using namespace std;
int n,M,a[maxn],i,j,k,mi[maxn],mx[maxn];
int check(int res){
for(i=1;i<=n;i++) mi[i]=mx[i]=-1;
k=1;
for(i=n;i>=1;i--){
while (a[k]+a[i]<M&&k<=n) k++;
if (k>n) break;
if (a[k]+a[i]<=M+res) mi[i]=i+k;
}
k=1;
for(i=n;i>=1;i--){
while (a[k]+a[i]<M&&k<=n) k++;
if (k>n) break;
if (a[k]+a[i]<=M+res){
while (k<n&&a[k+1]+a[i]<=M+res) k++;
mx[i]=i+k;
}
}
int l=0,r=2*n,mxl=0;
for(i=n;i>=1;i--) {
if (mi[i]<0||mx[i]<0) break;
l=max(l,mi[i]),r=min(r,mx[i]);
if (l>r) break;
if (l<=2*i-1&&2*i-1<=r) mxl=max(mxl,(n-i+1)*2);
}
k=n-mxl;
for(i=1;i<=k/2;i++) if (a[i]+a[k-i+1]>res) return 0;
return 1;
}
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d%d",&n,&M),n<<=1;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=0,r=M-1,mid,ans=M;
while (l<=r){
mid=(l+r)>>1;
if (check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d",ans);
}