• 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;
    }
    
  • 相关阅读:
    Azure School女神相邀,把每分钟都过的更充实
    Java、Node.js、PHP还是.Net? 无论你选谁,我都能教你一招!
    一样的Java,不一样的HDInsight大数据开发体验
    第五代微软小冰 | 你有一个来自人工智能的电话待接听
    2017“编程之美”终章:AI之战勇者为王
    大数据freestyle: 共享单车轨迹数据助力城市合理规划自行车道
    语音识别技术里程碑:错误率降至5.1%,超过专业速记员
    wait和waitpid
    Linux网络编程wait()和waitpid()的讲解
    如何测试Linux 中的wait函数能不能等待子进程的子进程?
  • 原文地址:https://www.cnblogs.com/int-2147483648/p/15526058.html
Copyright © 2020-2023  润新知