#4617. 逛公园

题目描述

小凯做题做累了,他想去逛公园。

公园里有 m m m 个亲子项目,每个项目一天只能一个家庭参加。一共有 n n n 个家庭,第 i i i 个家庭希望在第 l i l_i li r i r_i ri 天内参加恰好一次第 p i p_i pi 个项目。但是公园的工作人员很懒,他们希望上班的天数尽量少。某天要上班当且仅当至少有一个家庭参加了任意一个项目。

工作人员看到了小凯,想让他帮忙使得工作人员有更多咕咕咕的机会。但小凯又双叒叕不会了,所以他请你求出这个最少的上班时间。如果无论如何安排都不能达到要求,输出 GG。

数据范围

n ≤ 1 0 5 , l , r , m ≤ 1 0 9 n \le 10^5, l,r,m \le 10^9 n105,l,r,m109

题解

挺妙又难写的一道贪心题。

考虑每种类型能否满足,考虑到如果右端点都不同的话,那每个区间都取右端点就好了,于是考虑右端点相同的情况,那我们先让左端点最大的区间先确定这个右端点,剩下区间右端点左移即可,这样我们就可以把一个类型的区间的右端点变得互不相同。

考虑多个类型,首先我们先找到一个还没确定的区间中右端点最小的那个,那对于剩下的类型,如果存在左端点大于等于这个右端点的,那可以把这个区间确定在这个右端点上,如果有多个区间同时满足,根据贪心应该让右端点最小的被确定。

具体实现有点复杂(STL大法好)。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,t,ans,fl[N];
struct O{
	int l,r,x;
	friend bool operator < (const O& A,const O& B){
		return A.l<B.l;
	}
}p[N];
struct S{
	int l,r,x,i;
	friend bool operator < (const S& A,const S& B){
		return A.r<B.r;
	}
};
multiset<S>s;
priority_queue<O>q;
priority_queue<S>d[N];vector<O>e[N];
queue<int>h,H;
bool cmp(O A,O B){
	return A.x!=B.x?A.x<B.x:A.r>B.r;
}
int main(){
	cin>>n>>m;
	for (int i=1,l,r,x;i<=n;i++)
		scanf("%d%d%d",&l,&r,&x),
		p[i]=(O){l,r,x};
	sort(p+1,p+n+1,cmp);
	for (int i=1,j=0,r;i<=n;i=j+1){
		while(j<n && p[j+1].x==p[i].x) j++;
		t++;r=0;for (int k=i;k<=j;k++)
			p[k].x=t,r=max(p[k].r,r);
		for (int k=r,c=i;;k--){
			if (q.empty()) k=p[c].r;
			while(c<=j && p[c].r==k)
				q.push(p[c]),c++;
			if (!q.empty()){
				O u=q.top();u.r=k;q.pop();
				if (u.r<u.l) return puts("GG"),0;
				e[u.x].push_back(u);
			}
			if (c>j && q.empty()) break;
		}
	}
	for (int i=1,z,j=0;i<=t;i++){
		z=e[i].size();
		sort(e[i].begin(),e[i].end());
		for (int k=0;k<z;k++) p[++j]=e[i][k];
	}
	sort(p+1,p+n+1);
	for (int i=1;i<=n;i++)
		s.insert((S){p[i].l,p[i].r,p[i].x,i});
	for (int j=0,i;!s.empty();){
		S u=(*s.begin());ans++;s.erase(s.begin());fl[u.i]=1;
		while(j<n && p[j+1].l<=u.r){
			j++;if (fl[j]) continue;
			if (d[i=p[j].x].empty()) h.push(i);
			d[i].push((S){p[j].l,-p[j].r,p[j].x,j});
		}
		while(!h.empty()){
			int x=h.front();h.pop();
			while(!d[x].empty())
				if (fl[d[x].top().i]) d[x].pop();
				else break;
			if (d[x].empty()) continue;
			if (x==u.x){H.push(x);continue;}
			S v=d[x].top();v.r=-v.r;d[x].pop();
			s.erase(s.find(v));fl[v.i]=1;H.push(x);
		}
		h=H;while(!H.empty()) H.pop();
	}
	cout<<ans<<endl;return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值