Loading

[AHOI2014/JSOI2014]宅男计划

链接:https://www.luogu.com.cn/problem/P4040

题目描述:有\(n\)个食物,每个食物有一个价格\(p_{i}\)和一个保质期\(s_{i}\),\(JYY\)每天都要吃一个食物,它可以通过向外买小哥点外卖来获取食物,每叫一次外卖就要付\(f\)元。问若\(JYY\)\(M\)元,他最多能吃多少天的外卖。

题解:我们可以三分外卖小哥来的次数。现在就只用考虑外卖小哥来\(k\)次所能吃的最大天数了。

考虑对于一个价格为\(p_{i}\),保质期为\(s_{i}\)的食物与一个价格为\(p_{j}\),保质期为\(s_{j}\)的食物,若\(p_{i}<=p_{j}\)\(s_{i}>=s_{j}\),那么选\(j\)就不如选\(i\),所以我们可以排除掉这样的食物,可以按\(s\)排序后用单调栈维护。

现在\(p\)\(s\)都可以满足单调递增了,由于我们选择肯定是先吃是先吃\(s\)尽量小的,先买也是先买\(p\)尽量小的,所以我们可以从左到右依次买,买到买不了这一个时再买下一个,依次加入\(k\)个外卖小哥的贡献中。

但这样可能出现这这\(k\)个外卖小哥的贡献各不相同,但我们仔细想可以发现,加入\(k\)个外卖小哥的贡献各不相同,那么这一个食物一定是你最后买的,因为你没法买满这个食物,就更没法买满下一个价格比它更大的食物了。

复杂度\(O(nlogn)\)

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
	long long p,s;
	bool operator < (const node &a)const
	{
		return s<a.s;
	}
};
node point[600001];
long long n,f,m,deque[600001];
long long F(__int128 x)
{
	__int128 maxn,lst=0,cnt=0,M;
	M=m-f*x;
	if (M<0)
		return -1;
	int top=0;
	for (int i=1;i<=n;++i)
	{
		while (point[deque[top]].p>=point[i].p)
			top--;
		deque[++top]=i;
	}
	for (int i=1;i<=top;++i)
	{
		maxn=min(M/point[deque[i]].p,(point[deque[i]].s-lst+1)*x);
		if (maxn<=0)
			break;
		M-=maxn*point[deque[i]].p;
		lst+=maxn/x;
		cnt+=maxn; 
	}
	return cnt;
}
signed main()
{
	long long ans=0;
	__int128 first,last,mid,mid2; 
	cin>>m>>f>>n;
	first=1;
	last=m/f;
	for (int i=1;i<=n;++i)
		cin>>point[i].p>>point[i].s;
	sort(point+1,point+n+1);
	while (first<=last)
	{
		mid=first+(last-first)/3;
		mid2=last-(last-first)/3;
		if (F(mid)<F(mid2))
		{
			first=mid+1;
			ans=max(ans,F(mid));
		}
		else
		{
			last=mid2-1;
			ans=max(ans,F(mid2));
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2022-12-14 21:32  zhouhuanyi  阅读(73)  评论(0)    收藏  举报