[CSP-S 2021]廊桥分配
廊桥分配
题面
当一架飞机抵达机场时,可以停靠在航站楼旁的廊桥,也可以停靠在位于机场边缘的远机位。乘客一般更期待停靠在廊桥,因为这样省去了坐摆渡车前往航站楼的周折。然而,因为廊桥的数量有限,所以这样的愿望不总是能实现。
机场分为国内区和国际区,国内航班飞机只能停靠在国内区,国际航班飞机只能停靠在国际区。一部分廊桥属于国内区,其余的廊桥属于国际区。
\(L\) 市新建了一座机场,一共有 \(n\) 个廊桥。该机场决定,廊桥的使用遵循“先到先得”的原则,即每架飞机抵达后,如果相应的区(国内/国际)还有空闲的廊桥,就停靠在廊桥,否则停靠在远机位(假设远机位的数量充足)。该机场只有一条跑道,因此不存在两架飞机同时抵达的情况。
现给定未来一段时间飞机的抵达、离开时刻,请你负责将 \(n\) 个廊桥分配给国内区和国际区,使停靠廊桥的飞机数量最多。
\(1\leq n \leq 10^5,m_1、m_2\geq 1,m_1+m_2\leq 10^5\)
\(m_1\) 和 \(m_2\) 分别表示国内航班的数量与国际航班的数量。
分析
观察数据范围,显然我们大概需要一个 \(O(nlogn)\) 的做法,考虑怎么做。
原题面中本来附有一个表格,从表格统计的形式来看,猜测最后的答案统计是 \(O(n)\) 的,即我们需要求出分配 \(x\) 个廊桥给国内与国外能够停靠的飞机数量。
发现每个廊桥实际上相互之间不干扰,我们可以把飞机分成若干个组,每个组由同一个廊桥包办。
于是我们可以维护这样一个过程:
-
如果存在廊桥空闲,取用编号最小的空闲廊桥(尽量少的廊桥尽量多的值,故编号要选最小)。
-
若不存在,新开一组,增加一个廊桥。
以上过程我们可以用两个小根堆维护,一个维护目前不空闲的廊桥的状态,一个维护空闲廊桥的编号即可。
CODE
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
struct Brige{
int t,num;
friend bool operator < (const Brige &X,const Brige &Y ){
return X.t>Y.t; //小根
}
};
struct node{ int st,ed; }A[N],B[N];
struct Airport{ int a,b; }dp[N],sum[N];
int n,ans;
int x,y;
priority_queue<Brige> q;
priority_queue<int,vector<int>,greater<int> > id; //小根堆,储存目前空余编号
inline bool cmp(node a,node b) { return a.st<b.st; }
int main()
{
// freopen("airport.in","r",stdin);
// freopen("airport.out","w",stdout);
n=read(),x=read(),y=read();
for(register int i=1;i<=x;i++){
A[i].st=read(),A[i].ed=read();
}
for(register int i=1;i<=y;i++){
B[i].st=read(),B[i].ed=read();
}
sort(A+1,A+x+1,cmp),sort(B+1,B+y+1,cmp);
int cnt=0;
for(register int i=1;i<=x;i++){
while(q.size()){
Brige now=q.top();
if(now.t>A[i].st) break;
q.pop(); //空闲,弹出
id.push(now.num); //第num号桥空闲
}
if(id.size()){ //有桥空闲
int now=id.top(); id.pop();
dp[now].a++; //使用第now号桥飞机停泊量++
q.push((Brige){A[i].ed,now});
}
else{ //无桥空闲
cnt++; //新建立一个桥
dp[cnt].a++;
q.push((Brige){A[i].ed,cnt});
}
}
cnt=0;
while(!q.empty()) q.pop();
while(!id.empty()) id.pop();
for(register int i=1;i<=y;i++){
while(q.size()){
Brige now=q.top();
if(now.t>B[i].st) break;
q.pop(); //空闲,弹出
id.push(now.num); //第num号桥空闲
}
if(id.size()){ //有桥空闲
int now=id.top(); id.pop();
dp[now].b++; //使用第now号桥飞机停泊量++
q.push((Brige){B[i].ed,now});
}
else{ //无桥空闲
cnt++; //新建立一个桥
dp[cnt].b++;
q.push((Brige){B[i].ed,cnt});
}
}
for(register int i=1;i<=n;i++) sum[i].a=sum[i-1].a+dp[i].a;
for(register int i=1;i<=n;i++) sum[i].b=sum[i-1].b+dp[i].b;
for(register int i=0;i<=n;i++){
ans=max(ans,sum[i].a+sum[n-i].b);
}
printf("%d\n",ans);
return 0;
}