2020 ICPC North America Championship Problem G

Problem Link

statement

有数组A与数组B,你可以选择A的某个数与B的某个数搭配,每次搭配需要满足数组中每个数不重复选且\(a_i+b_i\leq s\),令\(d_i=abs(a_i-b_i)\),\(D=max(d_1,d_2,..,d_n)\),求完成n组匹配能得到的最小的D。

input

\(n\leq 2\times 10^5\)
\(a_i,b_i,s\leq 10^9\)

idea

啊,这题竟然比赛时没做出来quq...实在没想到怎么贪。
首先看到D是一堆数的最大,又要求D的最小,最大的最小与最小的最大很明显是二分答案的信号。我们二分每个D,对于数组A每个数x对应的B数组的数y需要满足

\begin{cases} &y\leq s-x\\ &y\leq D+x\\ &y\geq x-d \end{cases}

我们将A,B数组排序,按照以上不等式,每个A数组对应可选的B数组为一段序列,我们将以上序列排序后从左到右枚举B数组每个数。首先,如果当前的数在一段序列内,一定要拿,因为每次匹配都是相同的贡献,你留着不拿,给后面的数拿并不会增加贡献。其次,如果有多个序列段能进行匹配,一定要拿r(序列右端点)最小的,因为后面的数l一定比当前数大,所以l不能产生约束,但是当前拿r最小的,可能可以给后面更多的数得到匹配。所以就这么贪心地拿,在$O(n\log n)$内可以完成匹配。而二分答案次数为$O(\log s)$,故总时间复杂度为$O(n\log n\log s)$。

代码

/*************************************************************************
	> File Name: 3.cpp
	> Author: Knowledge_llz
	> Mail: 925538513@qq.com 
	> Blog: https://blog.csdn.net/Pig_cfbsl 
	> Created Time: 2020/10/5 14:20:58
 ************************************************************************/
#include<bits/stdc++.h>
#define For(i,a,b) for(register int i=(a);i<=(b);++i)
#define pb push_back
#define pr pair<int,int>
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define LL long long
using namespace std;
int read(){
	char x=getchar(); int u=0,fg=0;
	while(!isdigit(x)){ if(x=='-') fg=1; x=getchar(); }
	while(isdigit(x)){ u=(u<<3)+(u<<1)+(x^48); x=getchar(); }
	return fg?-u:u;
}
const int maxx=2e5+10;
int a[maxx],b[maxx],n,p,q,s;
struct cmp{
	bool operator () (const pr &a,const pr &b)const{
		return a.se>b.se;
	}
};
queue<pr>Q;
priority_queue<pr,vector<pr>,cmp>que;
bool check(int d){
	int tmp=0;
	while(!Q.empty()) Q.pop();
	while(!que.empty()) que.pop();
	For(i,1,p){
		int x=lower_bound(b+1,b+q+1,a[i]-d)-b,lim=min(a[i]+d,s-a[i]);
		int y=upper_bound(b+1,b+q+1,lim)-b-1;
		if(x<=y) Q.push(mk(x,y));
	}
	For(i,1,q){
		while(!Q.empty() && Q.front().fi<=i){
			que.push(Q.front());
			Q.pop();
		}
		while(!que.empty()){
			int r=que.top().se;
			que.pop();
			if(r<i)	continue; 
			++tmp;
			break;
		}
		if(tmp>=n) return tmp>=n;
	}
	return tmp>=n;
}
		
int main()
{
//	freopen("input.in","r",stdin);
//	freopen("output.out","w",stdout);
	n=read(); p=read(); q=read(); s=read();
	For(i,1,p) a[i]=read();
	For(i,1,q) b[i]=read();
	sort(a+1,a+p+1);
	sort(b+1,b+q+1);
	int l=0,r=1e9,mid,ans=-1;
	while(l<=r){
		mid=(l+r)>>1;
		if(check(mid)){ r=mid-1; ans=mid; }
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-10-05 23:22  Knowledge-Pig  阅读(184)  评论(0编辑  收藏  举报