LuoguP3602 Koishi Loves Segments

题面

n个区间和数轴上的m个关键点 (0<=n,m<=4*1e5,数轴范围 (-1^7) ~ (1^7))每个关键点有被区间区间覆盖的次数上限,求最多能放多少个区间到数轴上
传送门

题解

先把区间和关键点分别按从小到大排序(区间按左端点排序)

考虑贪心:

对于当前点来说,扫到一个区间时若已经达到上限,显然放弃右端点更大的区间更加优秀,因为在已经排序的情况下,这样做显然会对之后的关键点影响更小

所以具体做法是:

先排序,然后对于关键点维护它是否达到承受上限,若已达到承受上限则不断删除可覆盖它的右端点最大的区间直到满足限制为止

关于维护关键点的承受上限:

可以用一种支持动态插点排序(按区间右端点排序)的数据结构存下覆盖它的区间,然后删除最大右端点的区间即可

关于判断区间可否覆盖某关键点:

因为已经数据结构中的区间排过序,可以采用类似尺取法的思想(在数据结构中当前区间若是未覆盖这个关键点,则说明在此区间之前的区间也不行,所以可以保证任何区间都不会第二次进入数据结构)

关于这个妙妙的数据结构:

它叫multiset,很方便,很好用
推荐multiset学习文章: https://blog.csdn.net/sodacoco/article/details/84798621

代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define in inline
#define get getchar()
in int read()
{
	int t=0,x=1; char ch=get;
	while ((ch<'0' || ch>'9') &&ch!='-') ch=get;
	if(ch=='-') ch=get,x=-1;
	while (ch<='9' && ch>='0') t=t*10+ch-'0',ch=get;
	return t*x;
}
const int _=4e5+5;
struct region1{
	int l,r;
}e[_];
struct drop1{
	int num,a;
}d[_];
int n,m;

in int cmp1(drop1 a,drop1 b){return a.num<b.num;}
in int cmp2(region1 a,region1 b){return a.l<b.l;}
bool operator< (const region1 &a,const region1 &b)
{   return a.r<b.r;  }

multiset<region1> s; //s中存储当前关键点可能被覆盖的区间
int main()
{
	n=read(),m=read();
	int ans=n;
	for(re int i=1;i<=n;i++)
	{
		e[i].l=read(),e[i].r=read();
		if(e[i].l>e[i].r)swap(e[i].l,e[i].r);
	}
	for(re int i=1;i<=m;i++)
		d[i].num=read(),d[i].a=read();
	sort(d+1,d+1+m,cmp1);
	sort(e+1,e+n+1,cmp2);
	int j=1; //j 是当前区间
	for(re int i=1;i<=m;i++)
	{
		while (j<=n&&e[j].l<=d[i].num) s.insert(e[j++]); 
                //若当前区间可覆盖该点,则插入该区间
		while (s.size()!=0 && (*s.begin()).r<d[i].num)
                       s.erase(s.begin()); 
                // 删除s中不覆盖该点的区间(因为该点若不被覆盖,则接下来的点也不会被覆盖)
		while (s.size()>d[i].a) //该点超出限制
		{
			s.erase(--s.end()); 
                         //删除队尾区间,因为队尾区间是对后面的点影响最大的
			ans--; //统计答案
		}
	}
	cout<<ans<<endl;
}

原文地址:https://www.cnblogs.com/yzhx/p/12722945.html