codeforces 355F

题目描述:

原题来自 c o d e f o r c e s   355 F codeforces \ 355F codeforces 355F
A A A来一个超市买东西。
这个超市的部分商品在做活动,特别是显卡,推出了买一送一。
这个超市的活动规则是:以原价 P i P_i Pi购买一张显卡,可送一张价格严格低于 P i P_i Pi 的显卡。
超市总共有 n n n 张显卡,每张的价格为 P i P_i Pi,小 A A A很有钱,想要买下超市里所有的显卡。
请你告诉小A最少需要花多少钱。

输入样例1:

6 6 6
3   4   5   3   4   5 3 \ 4 \ 5\ 3 \ 4 \ 5 3 4 5 3 4 5

输出样例1:

14 14 14

数据范围:

n ≤ 1 0 5 , P i < 1 0 9 n \leq 10^5,P_i <10^9 n105,Pi<109


Solution:

考虑转换思路,由于所有显卡都要买,所以相当于最大化免费的显卡价格之和。
一个显然的思路时按价格从大到小排序贪心,可惜直接贪心不大对。
贪心的错误做法可以被样例 h a c k hack hack
首先,合并所有相同价格的显卡,得到(价格,数量)二元组,将二元组按照价格降序
排序,得到一个序列。维护一个价格的小根堆 H H H表示免费送的显卡。
依次处理序列中的每种价格的显卡, 设价格为 p p p, 数量为 m m m, 价格高于它的显卡有 p r e pre pre个。于是,当前显卡可以免费拿 m a x ( m i n ( p r e − 2 ∗ ∣ H ∣ , m ) , 0 ) max(min(pre − 2*|H|,m) , 0) max(min(pre2H,m),0) (因为有 ∣ H ∣ |H| H是对应相等个数的原来买的,所以全部 − 2 -2 2倍就是剩下买的,就是现在可以抵债的),剩下就需要买了。
买剩下的显卡时,比较一下 H H H堆首元素价格 p ′ p' p;如果 p > p ′ p > p′ p>p, 因为我们将 P i P_i Pi从大到小排序,所以 p ′ p' p一定是虚拟节点(下文会讲到),那么我们改为买 p ′ p′ p,于是多出来两个免费显卡空间,就可以免费拿两个当前显卡,并且买的显卡数量不变,血赚不亏。 (这种情况为什么会出现?后面会分析到。)
如果 p ≤ p ′ p≤ p′ pp,但是 2 p ≥ p ′ 2p ≥ p′ 2pp,将当前堆首显卡改为买,就可以免费拿现在的两个显卡。看起来血赚不亏,实际上买的显卡个数减少了 1 1 1(本来你要买当前的两个显卡,现在你只需要买 p ′ p′ p这个显卡), 为后面免费拿显卡的铺垫少了! 因此, 需要加入一个虚拟免费显卡 2 p − p ′ 2p −p′ 2pp到堆中,并留下当前的 p ′ p′ p!接下来如果买了这个虚拟免费显卡(前面的情况),意思就是还是选择原来的 p ′ p′ p显卡免费拿; 如果没有买这个虚拟免费显卡, 那么 2 p − p ′ + p ′ = 2 p 2p− p′+ p′ = 2p 2pp+p=2p,实际上就相当于我们用一个 p ′ p′ p
的显卡换了两个 p p p的显卡。
维护这个小根堆即可。最后,小根堆中的所有元素之和就是免费的显卡价格之和的最大
值。答案由 P i P_i Pi累加和减一下就可以了。
时间复杂度 O ( n   l o g   n ) O(n\ log \ n) O(n log n)


Code:

#include 
using namespace std;
#define int long long

const int N = 6e5 + 7, M = 5e3 + 7;
int n;
int a[N] = {};
struct edge{
	int p, m;
}b[N] = {};

priority_queue < int , vector <int> , greater <int> > q;
vector <int> now;

bool cmp(int x, int y) {
	return x > y;
}

signed main() {
	freopen("market.in","r",stdin);
	freopen("market.out","w",stdout);
	scanf("%lld", &n);
	long long ans = 0;
	for (int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
	sort(a + 1, a + n + 1, cmp);
	int cnt = 0;
	for (int i = 1; i <= n; i ++) {
		if (a[i] != a[i - 1]) b[++ cnt].p = a[i], b[cnt].m = 1;
			else ++ b[cnt].m;
	}
	int pre = 0;
	for (int i = 1; i <= cnt; i ++) {
		int num = q.size();
		int fre = max( min(pre - 2 * num, b[i].m), (long long)0);
		for (int j = 1; j <= fre; j ++) now.push_back(b[i].p);
		int buy = b[i].m - fre;
		for (int j = 1; j <= buy; j += 2) {
			if (q.empty()) break;
			int x = q.top();
			q.pop();
			if (b[i].p > x) {
				now.push_back(b[i].p);
				if (j != buy) {
					now.push_back(b[i].p);
				}
				continue;
			}
			now.push_back(x);
			if (2 * b[i].p >= x && j != buy) 
				now.push_back(2 * b[i].p - x);
		}
		for (int j = 0; j < now.size(); j ++)
			q.push(now[j]);
		now.clear();
		pre += b[i].m;
	}
	for (int i = 1; i <= n; i ++) ans = ans + a[i];
	while (!q.empty()) {
		int x = q.top();
		q.pop();
		ans = ans - x;
	}
	printf("%lld\n", ans);
	return 0;
}

你可能感兴趣的:(题解)