题目链接:https://ac.nowcoder.com/acm/problem/212915
到主站看:https://blog.csdn.net/weixin_43346722/article/details/109320382
题目
牛半仙的妹子被大魔王抓走了,牛半仙为了就他的妹子,前往攻打魔塔。
魔塔为一棵树,牛半仙初始在一号点。
牛半仙有攻击,防御,血量三个属性。
除一号点外每个点都有魔物防守,魔物也有攻击,防御,血量三个属性。
每个怪物后面都守着一些蓝宝石,获得 蓝宝石可增加
防。
牛半仙具有突袭属性,所以遇到魔物后会率先发动攻击,然后牛半仙和魔物轮换地攻击对方。
一个角色被攻击一次减少的血量是对方的攻击减去自己的防御。
当一个角色的血量小于等于 时,他就会死亡。
当牛半仙第一次到达某个节点时会与这个节点的魔物发生战斗。
当一个魔物死亡后,这个魔物所在的节点就不会再产生新的魔物。
现在牛半仙想知道他打死魔塔的所有魔物后的最大血量。
输入
第一行一个 代表节点数。
随后 行,每行两个数
,表示
与
节点有边相连。
随后一行,三个数,依次为勇士的血量、攻击、防御。
随后 行,每行四个数,依次为怪物的血量、攻击、防御,和其守着的蓝宝石数量。
输出
一个数,代表最大血量。如果牛半仙在打死魔塔的所有魔物之前就已经死亡了,则输出 。
样例输入
6 1 2 1 3 1 4 4 5 5 6 50000 10 0 35 54 2 4 25 55 3 5 21 51 4 5 20 64 5 3 43 64 6 1
样例输出
48901
样例解释
打怪的顺序依次为 4,3,5,2,6
可以证明不存在更优的方案。
数据范围
对于 的数据:
又有 的数据:
,且保证对于每一条边
,一定满足
对于前 的数据:
,且保证对于每一条边
,一定满足
或
对于最后 的数据:
,且保证对于每一条边
,一定满足
或
对于 的数据:有牛半仙血量
,攻击
,盔甲防御
。怪物血量为
,攻击
,防御
,打完一只怪后获得的蓝宝石数量为
至
思路
这道题是一道贪心。
首先我们观察数据,可得牛半仙是不会被打死,怪物的攻击不会被完全防御,牛半仙一定可以打死怪物等信息。
我们可以直接通过公式算出在没有蓝宝石的时候,打怪物要扣多少血。然后拿到宝石之后,再补回血量。
但是我们怎么确定打怪物的顺序呢?
其实我们可以让第一个打你的怪物的蓝宝石数除以打你的次数(我们叫它性价比)尽可能的小。
为什么呢?
我们要让这个蓝宝石在以后发挥更大的作用,就是要让打你次数多的放在后面。
那我们就维护一个大根堆(以性价比为关键字),每一次取出最大的那一个,然后就把能走到的没走过的点放进堆。而且加上能补回的血量。(就是加上一直以来获得的蓝宝石但不算这一次的,乘这个怪物打的次数)
然后最后输出剩下的血量就可以了。
比赛时
代码
#include<queue> #include<cstdio> #define ll long long using namespace std; struct node { int to, nxt; }e[200001]; priority_queue <pair<double, int> > a;//以性价比为关键字的大根堆 int n, x, y, le[100001], KK, fa[100001], fath[100001], X; ll blood[100001], at[100001], hold[100001], blue[100001], time[100001]; bool in[100001]; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } void dfs(int now, int father) { for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { fa[e[i].to] = now; dfs(e[i].to, now); } } int find(int now) { if (now == fath[now]) return now; return fath[now] = find(fath[now]); } int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); add(x, y); } fath[1] = 1; scanf("%lld %lld %lld", &blood[1], &at[1], &hold[1]); for (int i = 2; i <= n; i++) { fath[i] = i; scanf("%lld %lld %lld %lld", &blood[i], &at[i], &hold[i], &blue[i]); time[i] = blood[i] / (at[1] - hold[i]); if (blood[i] % (at[1] - hold[i]) == 0) time[i]--;//算出要打多少次 blood[1] -= (at[i] - hold[1]) * time[i];//假设没有蓝宝石,扣血 a.push(make_pair(1.0 * blue[i] / time[i], i)); } dfs(1, 0); in[1] = 1; while (!a.empty()) { int now = a.top().second; a.pop(); if (in[now]) continue; in[now] = 1; X = find(fa[now]); blood[1] += time[now] * blue[X];//补回蓝宝石保留的血量 time[X] += time[now]; blue[X] += blue[now]; if (!in[X]) a.push(make_pair(1.0 * blue[X] / time[X], X)); fath[now] = X;//并查集 } printf("%lld", blood[1]); return 0; }