ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CF335F】Buy One, Get One Free(带悔贪心)

2022-07-04 19:32:32  阅读:192  来源: 互联网

标签:Buy 带悔 策略 Get 2x FA 物品 FI define


题目链接

  • 有 \(n\) 个物品,你每购买一个物品可以免费获得一个价格严格小于它的物品,求得到所有物品的最小代价。
  • \(1\le n\le5\times10^5\)

带悔贪心

假如并非“严格小于”,而是“小于等于”,这就是 AGC001A

但我们依旧可以考虑排序后从大到小决策。不过由于现在的策略比较复杂,需要带悔贪心。

首先,如果仍然存在尚未配对的物品,直接拼上去一定不劣。

否则,假设当前有两个价值为 \(x\) 的物品,我们有以下策略:

  • 直接购买这两个价值为 \(x\) 的物品,代价为 \(2x\)。
  • 原来有一组价值为 \((a,b)\) 的物品,则我们可以考虑将 \((a,b)\) 拆开得到两组物品 \((a,x)\) 和 \((b,x)\),代价为 \(b\)。

如果 \(2x\le b\),肯定选择第一种策略。

如果 \(2x > b\),我们暂时先选择第二种策略,并考虑如何实现反悔,即如何将它转换成第一种策略。

发现从 \((a,x),(b,x)\) 到 \((a,b),x,x\) 代价为 \(2x-b\),所以只需要往堆里扔入一个 \(2x-b\) 的元素就可以表示这里的策略转换了。(而且,注意到这里策略转换和原本的选择第二种策略效果都是新产生两个尚未配对的物品,可以视作等价,因此不必特意区分。)

策略转换之后 \((a,b)\) 又变得可以拆开了,所以又要新建一个代价为 \(b\) 的物品,注意到 \(2x-b < b\)(因为 \(x < b\)),必然会先选 \(2x-b\) 再选 \(b\),可以一起扔到堆里。

但还要注意一个问题,就是有了表示策略转换的元素后,便可能出现堆中元素 \(v\) 比当前元素 \(x\) 小的情况。此时我们必然选择将 \(v\) 执行掉,但如果再想要执行策略转换代价为 \(2x-v > x\),所以不可能再反悔,只需往堆里扔入两个 \(x\) 即可(也可能只剩下一个 \(x\),则扔入一个 \(x\) 并标记增加一个新的尚未配对物品)。

代码:\(O(n\log n)\)

#include<bits/stdc++.h>
#define Cn const
#define CI Cn int&
#define N 200000
using namespace std;
namespace FastIO
{
	#define FS 100000
	#define Tp template<typename Ty>
	#define Ts template<typename Ty,typename... Ar>
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp void read(Ty& x) {x=0;while(!isdigit(oc=tc()));while(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int n,a[N+5],c[N+5],q[N+5],dc,dv[N+5];priority_queue<int,vector<int>,greater<int> > Q;
int main()
{
	int i;for(read(n),i=1;i<=n;++i) read(a[i]);sort(a+1,a+n+1);//排序
	for(i=1;i<=n;++i) dv[i]=a[i];dc=unique(dv+1,dv+n+1)-dv-1;
	int j=1;for(i=1;i<=n;++i) {while(dv[j]<a[i]) ++j;++c[j];}
	int t,w=0,ct=0;long long ans=0;for(i=dc;i;--i)//从大到小选择
	{
		t=0;while(c[i]&&w) --w,q[++ct]=dv[i],--c[i];//还存在尚未配对的物品
		while(c[i]&&!Q.empty()&&Q.top()<dv[i]) ans+=Q.top(),Q.pop(),q[++ct]=dv[i],--c[i],c[i]?(q[++ct]=dv[i],--c[i]):++w;//堆顶元素小于当前元素
		while(c[i]>=2&&!Q.empty()&&Q.top()<2*dv[i]) ans+=Q.top(),q[++ct]=2*dv[i]-Q.top(),q[++ct]=Q.top(),Q.pop(),c[i]-=2;//注意带悔
		while(ct) Q.push(q[ct--]);while(c[i]--) ans+=dv[i],++w;//把产生的元素加入堆里;剩余的物品尚未配对
	}return printf("%lld\n",ans),0;	
}

标签:Buy,带悔,策略,Get,2x,FA,物品,FI,define
来源: https://www.cnblogs.com/chenxiaoran666/p/CF335F.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有