P1752 点菜 题解
toolong114514 · · 题解
P1752 点菜 题解
upd on 2024/7/16:忘写时间复杂度了,补一下。
题面大意
传送门。
解题思路
注意到答案具备单调性。
如果花费
考虑对花费的周数
接下来要对
注意到这个时间限制可以转化为点菜的限制,所以下文默认
我们采取贪心的策略,让每个人都尽可能吃完他能吃的菜,且每个人吃的都不同(相同没有意义),判断最后是否能吃完。
依题意,按可以将客人分类按顺序讨论:
- 想吃好的人。
- 穷人。
- 正常人。
对于想吃好的人,先将菜品按美味度降序排序,再将每个人的下限按降序排序,将菜品不断甩进一个大根堆(以价格为值),每个人从堆里拿菜,直到拿
考虑证明其正确性:
按下限降序排序,使得前面的人选的菜,后面的人都能选,同时后面的人选的前面的人不一定能选,所以改变此方案一定不优。
容易发现此方案在只考虑想吃好的人时显然成立。
接下来讨论穷人。
对穷人,我们按每个人的价格上限升序排序,并对之前选剩的菜按价格升序排序,每个人按顺序那菜,直到拿满
与讨论想吃好的人的证明同理,改变此方案一定不优。
最后,计算正常人能点的最大菜数,如果大于等于剩菜数,
现在要将三部分的方案合起来,判断是否还是最优。
注意到想吃好的人已经将较贵的部分挑走了,所以无论是去补充没拿完的穷人还是和穷人交换,或者给正常人,都无法更优,所以我们已将想吃好的人和穷人的拿菜数最大化了。
同样,正常人的吃菜数也被最小化了,也是最优。
综上所述,我们证明了三个贪心的最优方案合起来是最优的。
所以,对
按上述思路编写代码即可。
时间复杂度约为 O(m\log^2m) ,可以通过本题。
代码难度至少严格弱于 P1084。
参考代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
const int N=5e5+10;
struct ccf{
int qul,num,cont;
friend bool operator<(ccf pre,ccf nxt){
return pre.num<nxt.num;
}
}dish1[N],dish2[N];
priority_queue<ccf> Q;
inline bool cmp1(ccf pre,ccf nxt){
return pre.qul>nxt.qul;
}
inline bool cmp2(ccf pre,ccf nxt){
return pre.num<nxt.num;
}
inline bool cmp3(int pre,int nxt){
return pre>nxt;
}
inline bool cmp4(int pre,int nxt){
return pre<nxt;
}
int n,m,p,q,ans=-1;
int lim1[N],lim2[N],cont[N];
bool vst[N];
inline bool check(int x){
memset(vst,0,sizeof(vst));
while(!Q.empty()) Q.pop();
int i,j,t=1,tmp=x;
for(i=1;i<=p;i++){
for(j=t;j<=m;j++){
if(dish1[j].qul>=lim1[i]){
Q.push(dish1[j]);
}else{
break;
}
}
t=j;
tmp=x;
while(!Q.empty()&&tmp>0){
vst[Q.top().cont]=true;
Q.pop();
tmp--;
}
}
int cnt1=0;
for(i=1;i<=m;i++){
if(!vst[dish1[i].cont]) dish2[++cnt1]=dish1[i];
}
sort(dish2+1,dish2+cnt1+1,cmp2);
tmp=x;j=1;
for(i=1;i<=cnt1;i++){
if(dish2[i].num>lim2[j]||!tmp){
tmp=x;
j++;
}
if(j>q) break;
vst[dish2[i].cont]=true;
tmp--;
}
int cnt2=0;
for(int i=1;i<=m;i++){
if(!vst[i]) cnt2++;
}
if((n-p-q)*x>=cnt2) return true;
else return false;
}
inline void read(int &x) {
x=0;int f=1;
char s=getchar();
for(;s<'0'||s>'9';s=getchar()) f=s=='-'?-f:f;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
x*=f;
}
signed main(){
dish1[0].cont=-1;
ios::sync_with_stdio(false);
read(n);read(m);read(p);read(q);
for(int i=1;i<=m;i++){
read(dish1[i].qul);read(dish1[i].num);
dish1[i].cont=i;
}
sort(dish1+1,dish1+m+1,cmp1);
for(int i=1;i<=p;i++){
read(lim1[i]);
}
sort(lim1+1,lim1+p+1,cmp3);
for(int i=1;i<=q;i++){
read(lim2[i]);
}
sort(lim2+1,lim2+q+1,cmp4);
int ll=1,rr=m;
while(ll<=rr){
int mid=(ll+rr)/2;
if(check(mid)){
ans=mid;
rr=mid-1;
}else{
ll=mid+1;
}
}
cout<<ans;
return 0;
}
本代码可以通过洛谷评测。
Written by toolong114514 on 2024/7/15.