CF335F 题解

\(CF335F\) \(Buy\) \(One\),\(Get\) \(One\) \(Free\)

题意:一家馅饼店买馅饼。规则是每全价购买一个馅饼,都可以免费得到一个价格严格更低的馅饼。求出为 \(n\) 个馅饼支付的最小花费。

\(n \leq 5\times 10^5\)\(1\leq a_i \leq 10^9\)

\(sol\):考虑可撤销贪心+小根堆维护。

首先价格相同的馅饼可以放到一起考虑,从大到小排序后考虑每种不同价格的馅饼。则第 \(i\) 种最多白嫖的个数为 \(p=\min(c_i,num-2*sum)\) ,其中 \(c_i\) 为馅饼个数,\(num\) 为已经考虑的更贵的馅饼的总数,\(sum\) 为前面决定白嫖的馅饼数量。这样不一定最优,但不慌,我们还可以反悔。我们把要白嫖的馅饼的贡献(省的钱)放入一个小根堆里。现在对于第 \(i\) 种我们要处理剩下 \(\min(c_i,num)-p\) 的白嫖机会。

每次我们取出堆顶元素 \(k\)

  • 如果 \(k\ge val_i\) 说明仍然白嫖 \(k\) 更优。但是如果这时第 \(i\) 种馅饼还剩至少 \(2\) 个,并且\(2\times val_i-k>=0\) 说明我们还可以选择不买 \(k\) 这个馅饼,而用他白嫖一个当前馅饼,而原来用来白嫖 \(k\) 的馅饼也能提供一次白嫖的机会,而要撤销白嫖 \(k\) 的操作可以向堆中加入一个权值为 \(2val_i-k\) 的元素。

  • 如果 \(k<val_i\) ,则白嫖当前馅饼更优,如果如果这时第 \(i\) 种馅饼还剩至少 \(2\) 个,则我们还可以再白嫖一个,这里需要注意的是堆中 \(k\) 表示的贡献不一定是原本价格,所以虽然 \(k<val_i\) 但因为我们是从大到小处理,所以 \(k\) 所对应的馅饼价格还是一定比 \(val_i\) 贵,也就是说它依然可以提供一次白嫖当前馅饼的机会。

一个细节,每次新加入的贡献不能直接加入堆中,原理很简单,处理当前馅饼时,堆中只能有之前馅饼的贡献,所以需要先放入一个栈中转存一下,处理结束了再加入堆。最后堆里元素的和就是所有白嫖的馅饼。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500005;
char buf[1<<21],*p1,*p2;
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline ll rd()
{
	char c;ll f=1;
	while((c=gc())<'0'||c>'9')if(c=='-')f=-1;
	ll x=c^48;
	while('0'<=(c=gc())&&c<='9')x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
ll n,ans,a[N];
ll val[N],c[N],cnt;
ll st[N],top;
priority_queue<ll,vector<ll>,greater<ll> >q;
int cmp(ll a,ll b){return a>b;}
int main()
{
	n=rd();
	for(int i=1;i<=n;i++)
	  ans+=(a[i]=rd());
	sort(a+1,a+n+1,cmp);a[0]=1e18;
	for(int i=1;i<=n;i++)
	{
		if(a[i]!=a[i-1]) val[++cnt]=a[i];
		c[cnt]++; 
	}
	ll p,tot,num=0;
	for(int i=1;i<=cnt;i++) 
	{
		p=min(num-2*(ll)q.size(),c[i]); 
		tot=min(c[i],num)-p;
		for(int j=1;j<=p;j++)
		  st[++top]=val[i];
		for(int j=1;j<=tot;j+=2)
		{
			int k=q.top();
			q.pop();
			if(k<val[i])
			{
				st[++top]=val[i];
				if(j<tot) st[++top]=val[i]; 
			}
			else
			{
				st[++top]=k;
				if(j<tot&&2*val[i]-k>=0)
					st[++top]=2*val[i]-k; 
			}
		}
		while(top) q.push(st[top--]);
		num+=c[i];
	}
	while(!q.empty())
	  ans-=q.top(),q.pop();
	cout<<ans; 
	return 0; 
}
posted @ 2024-12-17 17:22  Re_Star  阅读(87)  评论(1)    收藏  举报