#贪心,二叉堆,二分答案#洛谷 1752 点菜

题目

\(n\) 个人,\(m\) 道菜,每道菜都有价格和美味度,每天每个人最多选择一道菜。

其中有 \(p\) 个挑剔的人,他们要求美味度不小于自己的阈值

\(q\) 个拮据的人,他们要求价格不超过自己的阈值,还有 \(n-p-q\) 个随心所欲的人。

问至少多少天所有菜都能被点一遍,无解输出 -1


分析

直接求天数比较困难,考虑二分答案,转为判断一定天数内是否完成。

先考虑美味度的限制,那么按美味度降序排序,挑剔之人阈值降序排序,

然后每个挑剔之人选择 \(mid\) 个价格较大的,然后只剩下拮据的人,一样降序排序挑选即可。

如果剩余菜数不超过 \((n-A-B)*mid\) 那么即为合法。


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=200011;
struct rec{int x,y;}a[N];
int x[N],y[N],heap[N],n,Cnt,A,B,m;
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool cmp(rec x,rec y){return x.x>y.x;}
void Swap(int &x,int &y){x^=y^=x^=y;}
void Push(int w){
	int x=++Cnt; heap[x]=w;
	for (;x>1;x>>=1)
	if (heap[x]<=heap[x>>1]) return;
	    else Swap(heap[x],heap[x>>1]);
}
void Pop(){
	heap[1]=heap[Cnt--];
	for (int x=1;(x<<1)<=Cnt;){
		int y=x<<1|1;
		if (y>Cnt||heap[x<<1]>heap[x<<1|1]) --y;
		if (heap[x]>=heap[y]) return;
		    else Swap(heap[x],heap[y]),x=y;
	}
}
bool check(int mid){
	if (1ll*n*mid>=m) return 1;
	int j=1,rest=n*mid; Cnt=0;
	for (int i=A;i;--i){
		for (;j<=m&&a[j].x>=x[i];++j) Push(a[j].y);
		int now=Cnt<mid?Cnt:mid;
		while (now--) Pop();
	}
	for (;j<=m;++j) Push(a[j].y);
	for (int i=B;i&&Cnt;--i){
		int now=Cnt<mid?Cnt:mid;
		for (;now&&Cnt;Pop())
		if (heap[1]>y[i]){
			--rest;
			if (rest==-1) return 0;
		}else --now;
	}
	return Cnt<=rest;
}
int main(){
	n=iut(),m=iut(),A=iut(),B=iut(),n-=A+B;
	for (int i=1;i<=m;++i) a[i]=(rec){iut(),iut()};
	for (int i=1;i<=A;++i) x[i]=iut(); sort(x+1,x+1+A);
	for (int i=1;i<=B;++i) y[i]=iut(); sort(y+1,y+1+B);
	sort(a+1,a+1+m,cmp);
	int l=1,r=m+1;
	while (l<r){
		int mid=(l+r)>>1;
		if (check(mid)) r=mid;
		    else l=mid+1; 
	}
	if (l==m+1) printf("-1");
	    else printf("%d",l);
	return 0;
}
posted @ 2021-12-22 15:02  lemondinosaur  阅读(27)  评论(0编辑  收藏  举报