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;
}
posted @ 2021-11-08 20:38  _Veritas  阅读(144)  评论(0编辑  收藏  举报