1 条题解

  • 0
    @ 2025-8-24 21:28:41

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Mortidesperatslav
    岂不日戒,玁狁孔棘。

    搬运于2025-08-24 21:28:40,当前版本为作者最后更新于2024-02-05 14:03:46,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    脑洞题。绝世好题。

    首先,我们经过分析可以得出贪心策略:让挑剔的人选美味的,让贫穷的人点价廉的。

    这也是本题的核心思路。

    然后我们可以发现,如果 kk 周可以点完所有菜,那么 k+1k+1 周也能点完所有菜。

    根据数学归纳法,推出:如果 kk 周可以点完所有菜,那么若 n>kn>knn 周一定能点完所有菜。

    看起来上面的性质都非常简单,但实际上这些是解决这道题目的关键。

    我们推出来了,如果 kk 天可以点完所有菜,那么若 n>kn>knn 天一定能点完所有菜。那么我们二分查找最小的 kk,不就可以解决了吗?

    二分的板子看这篇题解的人应该都会。二分都不会还做什么紫题关键在于判断函数怎么写。

    首先,如果除去这些贫穷的人和挑剔的人,其他人都已经可以把菜选完了,那当然能把菜都选一遍。

    然后我们进行贪心,即让挑剔的人选美味的,让贫穷的人点价廉的:

    先把菜按照美味度排序。放到一个大根堆里,然后枚举挑剔的人,注意先把这些人喜欢的美味度按从大到小排。枚举过程很简单,开一个大根堆(推荐用 priority_queue),把每个挑剔的人喜欢的菜放进堆里,放完之后弹出直到堆空了或者是已经弹出了 kk(当前二分校验的值)个元素。

    因为挑剔的人从大到小排,在堆里的菜一定是让下一个人满意的。

    这样挑剔的人就解决了。

    剩下贫穷的人同理,把没取完的菜放入一个数组,按价格从小到大排序,然后放到任意队列里(当然为了思路更清晰,可以另开一个小根堆进行如上操作)。

    最后算一算有多少道菜没取完。判断一下剩下的人能不能把这些菜点完。

    然后,这道题就轻松地解决了!

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,p,q,b[114514<<2],c[114514<<2];
    struct node{
    	int x,y;
    	bool operator<(node b)const{
    		return this->y < b.y;
    	}
    }a[114514<<2],pp[114514<<2];
    priority_queue<node>qq;
    bool cmp(node x,node y){
    	return x.x>y.x;
    }
    bool check(int k){
    	if((long long)(n-p-q)*k>=m)return 1;//不需要挑剔和贫穷的人就可以取完
    	while(qq.size())qq.pop();//多测清空,因为函数最后直接加上 size 了,下一次调用不清空会出错
    	int top=1;
    	for(int i=1;i<=p;i++) {
    		while(top<=m&&a[top].x>=b[i])qq.push(a[top++]);
    		for(int j=1;j<=k&&qq.size();j++)qq.pop(); 
    	}//枚举挑剔的人
    	int cnt=0;
    	while(qq.size()){
    		pp[++cnt]=qq.top();
    		qq.pop();
    	} 
    	for(int i=top;i<=m;i++)pp[++cnt]=a[i];
    	sort(pp+1,pp+cnt+1);//存入数组,按价格升序排序
    	top=1;
    	for(int i=1;i<=q;i++) {
    		while(top<=cnt&&pp[top].y<=c[i])qq.push(pp[top++]);
    		for(int j=1;j<=k&&qq.size();j++)qq.pop();
    	}//枚举贫穷的人
    	int res=qq.size()+cnt-top+1;
    	return(res<=(n-p-q)*k);//判断剩下的人能不能取完
    }
    int main(){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin>>n>>m>>p>>q;
    	for(int i=1;i<=m;i++)cin>>a[i].x>>a[i].y;
    	for(int i=1;i<=p;i++)cin>>b[i];
    	for(int i=1;i<=q;i++)cin>>c[i];
    	sort(b+1,b+p+1);
    	reverse(b+1,b+p+1);
    	sort(c+1,c+q+1);
    	sort(a+1,a+m+1,cmp);//全部排序
    	int l=1,r=m,ans=-1;//设为 -1 可以直接判无解
    	while(l<=r){//二分板子
    		int mid=(l+r)>>1;
    		if(check(mid))ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	cout<<ans;
    }
    

    题目出的真好,我开始感觉没有头绪,但细细拆分就变成了一道不难的题目,赞美伟大出题人!

    • 1

    信息

    ID
    727
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者