#贪心,二叉堆,二分答案#洛谷 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;
}