CF 1398E Two Types of Spells(set,贪心)
题目大意
有两种技能“电”和“火”,每种技能都有一个伤害值d,同时技能“电”可以使下一个使用的技能的伤害值提高一倍。每次你都会学习一个技能或者遗忘一个技能,问每次通过合理安排技能使用顺序能够造成最大伤害值是多少 。
解题思路
首先考虑如何安排顺序使伤害值最大化,我们可以考虑将技能火按伤害值排序,然后将技能电按照从大到小的顺序插入技能火中。第一个插入的技能电一定会放在伤害值最大的技能火前面,然后用这个电的伤害替代后面火的伤害。再插入一个技能电,放在当前伤害最大的前面,再用插入的电的伤害替代后面技能的伤害。如此操作下来一定是最优的。再进一步考虑,我们发现那些被伤害被加倍的技能(假设电的个数为a个,也就是说最多a个技能能被加倍)一定包括一个伤害值最大的技能火,还有在剩下的技能中最大的a-1个技能。
代码实现
为了方便维护被加倍的技能,我们只用维护伤害值最大的a个技能,如果这些技能全是电,那么我们就用伤害值最大的火替换掉这a个中伤害值最小的电。由于要支持删除操作,只用堆来维护是不行的,要改用set。具体细节请参考代码:
#include <bits/stdc++.h>
using namespace std;
long long sum, pre;
set<int> db, oc;
// db技能电,oc技能火
set<int> bg, sm;
// bg维护被加倍的技能 sm维护其它技能
void add(int d, int op) {
sum += d;
if (op == 1) db.insert(d);
else oc.insert(d);
if (sm.empty() || d > *prev(sm.end())) bg.insert(d), pre += d;
else sm.insert(d);
}
void del(int d, int op) {
sum -= d;
if (op == 1) db.erase(d);
else oc.erase(d);
if (bg.count(d)) bg.erase(d), pre -= d; else sm.erase(d);
}
void maintain() {
if (bg.size() > db.size()) {
sm.insert(*bg.begin());
pre -= *bg.begin();
bg.erase(bg.begin());
}
if (bg.size() < db.size()) {
bg.insert(*prev(sm.end()));
pre += *prev(sm.end());
sm.erase(prev(sm.end()));
}
}
int main() {
int n;
scanf("%d", &n);
for (int d, op, i = 1; i <= n; i++) {
scanf("%d%d", &op, &d);
if (d < 0) del(-d, op);
else add(d, op);
maintain();
if (oc.empty() && db.empty()) puts("0");
else if (oc.empty() || (!db.empty() && *db.begin() >= *prev(oc.end()))) {
long long ans = pre + sum - *db.begin();
if (oc.size()) ans += *prev(oc.end());
printf("%lld\n", ans);
}
else printf("%lld\n", pre + sum);
}
return 0;
}