CSP-S 2021 廊桥分配 题解
Problem
Solution
看到 “所有 \(a_{1, i}, b_{1, i}, a_{2, i}, b_{2, i}\) 为数值不超过 \({10}^8\) 的互不相同的正整数”,容易想到离散化。
然后显然我们可以对国内和国际两种航班分别考虑,求出dp[i][0/1]=给国内/国际分配i个栈桥时能够停靠的航班数
,即可 \(O(n)\) 得到答案。
对于dp[i][0/1]
可以先求出f[i][0/1]=需要至少i个栈桥才能停靠的国内/国际航班数
,然后对f
做前缀和即为dp
。
我们将航班按左端点排序。
维护一个时间轴上的数组num
,初始值全为 \(inf\) 。用 cnt
统计已经使用过的栈桥
对于第一个航班,记录它停靠在1号栈桥(++dp[1][0]
),然后将右端点的num
设为1
。
对于后面的航班,查询其左端点以左的num
的最小值,记录它停靠在该栈桥,将最小值所在位置的num
值设为 \(inf\) ,然后将右端点的num
设为该栈桥编号(若最小值为 \(inf\) 则记录它停靠在 ++cnt
)。
于是我们只需写一个线段树维护 单点修改、单点查询、区间(其实是前缀)查询最小值位置 即可。
不过我考场上还有半小时结束的时候想出来的,最后没调出来
进一步观察发现,由于航班按左端点排序,所以每次的“前缀查询最小值位置”其实是单调的,且每次查到即销毁该最小值。所以实际上,我们只需要用堆来维护这个过程即可。
即,每次在左端点取出堆顶元素,然后在右端点插入,若堆为空则记录它停靠在 ++cnt
。
Code
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline int read(){
int x=0;bool f=false;char c=getchar();
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
if(c=='-') f=true,c=getchar();
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f?-x:x;
}
const int N=200005;
int n,m1,m2,q[N],cnt,num[N],zq[N],tot,dp1[N],dp2[N];
struct Plane{int a,b;}p[N];
map<int,int> mapp;
priority_queue<int> Q1,Q2;
int main(){
n=read();m1=read();m2=read();
for(int i=1;i<=m1;++i) q[++cnt]=p[i].a=read(),q[++cnt]=p[i].b=read();
sort(q+1,q+2*m1+1);
for(int i=1;i<=2*m1;++i) mapp[q[i]]=i;
for(int i=1;i<=m1;++i) p[i].a=mapp[p[i].a],p[i].b=mapp[p[i].b],num[p[i].a]=i,num[p[i].b]=-i;
// for(int i=1;i<=m1;++i) cout<<p[i].a<<' '<<p[i].b<<endl;
for(int i=1;i<=2*m1;++i){
if(num[i]<0) Q1.push(-zq[-num[i]]);
else
if(!Q1.empty()) zq[num[i]]=-Q1.top(),Q1.pop();
else zq[num[i]]=++tot;
}
for(int i=1;i<=m1;++i) if(zq[i]<=n) ++dp1[zq[i]];
for(int i=2;i<=n;++i) dp1[i]+=dp1[i-1];
// for(int i=1;i<=n;++i) cout<<dp1[i]<<' '; cout<<endl;
memset(q,0,sizeof(q));memset(p,0,sizeof(p));memset(num,0,sizeof(num));memset(zq,0,sizeof(zq));
mapp.clear(); tot=0; cnt=0;
for(int i=1;i<=m2;++i) q[++cnt]=p[i].a=read(),q[++cnt]=p[i].b=read();
sort(q+1,q+2*m2+1);
for(int i=1;i<=2*m2;++i) mapp[q[i]]=i;
for(int i=1;i<=m2;++i) p[i].a=mapp[p[i].a],p[i].b=mapp[p[i].b],num[p[i].a]=i,num[p[i].b]=-i;
for(int i=1;i<=2*m2;++i){
if(num[i]<0) Q2.push(-zq[-num[i]]);
else
if(!Q2.empty()) zq[num[i]]=-Q2.top(),Q2.pop();
else zq[num[i]]=++tot;
}
for(int i=1;i<=m2;++i) if(zq[i]<=n) ++dp2[zq[i]];
for(int i=2;i<=m2;++i) dp2[i]+=dp2[i-1];
// for(int i=1;i<=m2;++i) cout<<zq[i]<<' '; cout<<endl;
int ans=0;
for(int i=0;i<=n;++i) ans=max(ans,dp1[i]+dp2[n-i]);
printf("%d",ans);
return 0;
}