CSP-S2021试题T1廊桥分配详讲

来迟了来迟了,忘写了。
我们来看看这道卡了本蒟蒻3个小时的题。
传送门luoguP7913廊桥分配
题目大意:分为了国内和国外两部分,飞机来了要么停廊桥要么走,有空位一定停,且停最小的,要如何分配廊桥才能使停的飞机数最大。
首先我们要知道一个事实:

对于n架飞机,有无数个廊桥,其中的一架飞机k,能够停在廊桥的要求是总的廊桥数大于等于从第一架来的飞机到它飞来时使它能停到廊桥的廊桥数。
换而言之,对于某一架飞机,使它能停靠的廊桥数与有多少个廊桥无关。
至于为什么,我们可以思考:
尽管有无数个廊桥,但是飞机是严格按照停靠在最小的廊桥上,只要能满足前k架飞机能全部停廊桥数,那么其余的廊桥是没有用上的。

得到这个事实我们可以得到:
可以就用给出的总廊桥数来模拟,对于每一个廊桥,可以统计出在这个廊桥有多少个飞机停,所以总飞机停靠个数就是1~k个廊桥上的飞机总和,这个只需要前缀和处理即可,最后枚举分配给国内,国外的廊桥数,并加到一起,更新max即可。
我们应该如何模拟飞机离开和进入呢?
这里使用两个优先队列来分别维护最小编号的廊桥和所在哪个廊桥的飞机离开时间。
首先先按飞机来的时间进行排序,将编号最小的廊桥给它,删除该廊桥,再将它的离开时间和该廊桥编号压入另一小根堆中,并以离开时间为第一关键字来排序。
对于每一架来的飞机,比较它来的时间和堆中飞机离开时间,若离开时间小于了来的时间,那么就说明该飞机已经离开,删除该飞机,并又将它所在的廊桥编号压入堆中,循环直达堆顶飞机的离开时间大于了它来的时间。
然后就又重复操作,取出最小编号的廊桥,删除,将该廊桥和它离开时间一同压入堆中即可。
处理完后,我们得到每一廊桥停靠的飞机数量,那么统计前缀和即是有这么多个廊桥能停靠的飞机总数量。枚举比较即可。
时间复杂度O(nlog2n)
代码实现:

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m1,m2;
priority_queue<int>id;//最小廊桥编号 
priority_queue<pair<int,int> >q;//飞机离开时间和它所在的廊桥编号 
int cfa[N],cfb[N];//统计该廊桥有多少个飞机停 
struct node
{
    
    
	int l,r;
}a[N];

bool comp(node x,node y)
{
    
    
	return x.l<y.l;
}

int main()
{
    
    
	scanf("%d%d%d",&n,&m1,&m2);
	for(int i=1;i<=m1;i++) scanf("%d%d",&a[i].l,&a[i].r);
	sort(a+1,a+m1+1,comp);
	for(int i=1;i<=n;i++) id.push(-i);
	for(int i=1;i<=m1;i++)
	{
    
    
		while(!q.empty() && -q.top().first<a[i].l)//飞机已经离开 
		{
    
    
			id.push(-q.top().second);
			q.pop();
		}
		if(!id.empty())//还有廊桥可以分配 
		{
    
    
			cfa[-id.top()]++;
			q.push(make_pair(-a[i].r,-id.top()));
			id.pop();
		}
	}
	for(int i=1;i<=n;i++) cfa[i]+=cfa[i-1];
	
	while(!id.empty()) id.pop();while(!q.empty()) q.pop();//初始化 
	
	for(int i=1;i<=m2;i++) scanf("%d%d",&a[i].l,&a[i].r);
	sort(a+1,a+m2+1,comp);
	for(int i=1;i<=n;i++) id.push(-i);
	for(int i=1;i<=m2;i++)
	{
    
    
		while(!q.empty() && -q.top().first<a[i].l) 
		{
    
    
			id.push(-q.top().second);
			q.pop();
		}
		if(!id.empty())
		{
    
    
			cfb[-id.top()]++;
			q.push(make_pair(-a[i].r,-id.top()));
			id.pop();
		}
	}
	for(int i=1;i<=n;i++) cfb[i]+=cfb[i-1];
	
	int ans=0;
	for(int i=0;i<=n;i++) ans=max(ans,cfa[i]+cfb[n-i]);//枚举分配给国内国外多少时最大 
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/pigonered/article/details/120989039
t1