廊橋分配
題面
當一架飛機抵達機場時,可以停靠在航站樓旁的廊橋,也可以停靠在位於機場邊緣的遠機位。乘客一般更期待停靠在廊橋,因為這樣省去了坐擺渡車前往航站樓的周折。然而,因為廊橋的數量有限,所以這樣的願望不總是能實現。
機場分為國內區和國際區,國內航班飛機只能停靠在國內區,國際航班飛機只能停靠在國際區。一部分廊橋屬於國內區,其余的廊橋屬於國際區。
\(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;
}