T1 廊桥分配
解题思路:
wss:NOIP与CSP第一题基本都可以通过手♂玩发现一些性质与结论。那我们就先来手玩一下这个题的数据。
我们通过手玩可以发现,首先有一个关键结论:如果给廊桥编上号,每当一架飞机到达后,如果强制让它进入编号最小的廊桥,这样不会影响进入廊桥飞机的集合,且当廊桥总数增加后,原本在哪个廊桥的飞机,仍旧会进入原来的廊桥。
这个性质也很好理解:新加入廊桥可以视为原先的远机位的一部分,进入新加的这个廊桥的飞机可以视为在原来该进入远机位的飞机中贪心选择一轮。
我们先忽略廊桥数量的限制来安排航班。维护一个空闲的廊桥队列,每到达一架航班,就给它安排编号最小的廊桥供其使用。
现在加上廊桥数量的限制。容易发现刚才的廊桥分配方法直接就帮我们解决了廊桥限制的问题:如果当前有 n n n个廊桥可供使用,则分配到 n + 1 n+1 n+1号及以后的廊桥实质上就是分配到远机位了,不需要再做任何额外的处理。
到这里做法就很清晰了:我们按照开头提到的分配方法来安排航班的停靠位置,记录各廊桥停靠的航班数,做一个前缀和,最后枚举分配给某个区的廊桥数,算出各情况下两区实际使用廊桥的航班数总和,即可解决本题。
代码:
#include<bits/stdc++.h>
#define in read()
#define MAXN int(1e5+50)
#define pii pair<int,int>
using namespace std;
struct node{
int x,y;
bool operator < (const node &rhs) const{
return x<rhs.x;
}
}a[MAXN],b[MAXN];
int n,m1,m2,arr1[MAXN],arr2[MAXN];
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
inline void prework(node t[],int m,int arr[]){
priority_queue<int,vector<int>,greater<int> >q1;// 空闲廊桥队列
priority_queue<pii,vector<pii>,greater<pii> >q2;// 等待离港航班队列
for(int i=1;i<=n;i++)q1.push(i);
for(int i=1;i<=m;i++){
while(!q2.empty() and t[i].x>=q2.top().first){
q1.push(q2.top().second);
q2.pop();
}
if(q1.empty())continue;
int chs=q1.top();
q1.pop();arr[chs]++;
q2.push(make_pair(t[i].y,chs));
}
for(int i=1;i<=n;i++)arr[i]+=arr[i-1];
}
int main(){
n=in,m1=in,m2=in;
for(int i=1;i<=m1;i++)a[i].x=in,a[i].y=in;
for(int i=1;i<=m2;i++)b[i].x=in,b[i].y=in;
sort(a+1,a+m1+1),sort(b+1,b+m2+1);
prework(a,m1,arr1),prework(b,m2,arr2);
int ans=0;
for(int i=0;i<=n;i++)
ans=max(ans,arr1[i]+arr2[n-i]);
cout<<ans<<'\n';
return 0;
}