2020 ICPC North America Championship Problem G
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;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 2025成都.NET开发者Connect圆满结束
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络