链接
D − 牛 半 仙 的 魔 塔 D-牛半仙的魔塔 D−牛半仙的魔塔
题目描述
牛半仙的妹子被大魔王抓走了,牛半仙为了就他的妹子,前往攻打魔塔。
魔塔为一棵树,牛半仙初始在一号点。
牛半仙有攻击,防御,血量三个属性。
除一号点外每个点都有魔物防守,魔物也有攻击,防御,血量三个属性。
每个怪物后面都守着一些蓝宝石,获得1蓝宝石可增加1防。
牛半仙具有突袭属性,所以遇到魔物后会率先发动攻击,然后牛半仙和魔物轮换地攻击对方。
一个角色被攻击一次减少的血量是对方的攻击减去自己的防御。
当一个角色的血量小于等于 0 时,他就会死亡。
当牛半仙第一次到达某个节点时会与这个节点的魔物发生战斗。
当一个魔物死亡后,这个魔物所在的节点就不会再产生新的魔物。
现在牛半仙想知道他打死魔塔的所有魔物后的最大血量。
输入
第一行一个 n 代表节点数。
随后 n-1 行,每行两个数 i,j,表示 i 与 j 节点有边相连。
随后一行,三个数,依次为勇士的血量、攻击、防御。
随后 n-1 行,每行四个数,依次为怪物的血量、攻击、防御,和其守着的蓝宝石数量。
输出
一个数,代表最大血量。如果牛半仙在打死魔塔的所有魔物之前就已经死亡了,则输出 -1。
样例输入
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
可以证明不存在更优的方案。
对于20%的数据:n≤15
又有30%的数据:n≤1000,且保证对于每一条边 (i,j),一定满足 i=1
对于前90%的数据:n≤1000,且保证对于每一条边(i,j),一定满足 j=i+1 或 i=1
对于最后10%的数据:n≤100000,且保证对于每一条边(i,j),一定满足 j=i+1 或 i=1
对于100%的数据:有牛半仙血量<5*10^18,攻击=2000,盔甲防御=0。
怪物血量为3000~10^6 攻击5 * 10^5- 7 * 10^5
防御≤1000,打完一只怪后获得的蓝宝石数量为1至5
思路
因为牛半仙攻击一定,那么打一只怪的次数是不变的
那么我们考虑攻打点的先后顺序
设性价比为
打
这
只
怪
获
得
的
蓝
宝
石
数
量
攻
打
一
只
怪
所
需
次
数
\frac{打这只怪获得的蓝宝石数量}{攻打一只怪所需次数}
攻打一只怪所需次数打这只怪获得的蓝宝石数量
然后我们用一个并查集,如果当前点的父亲节点性价比比它儿子小,就并成一个点
然后我们从1号点出发,每次将可以到达的点加入大根堆维护就好了
考场上没想到这种做法, 只想到了性价比,但是没有想到大根堆还有并查集,痛失不知道多少分,血亏
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
priority_queue < pair < double, int > > Q;
int n, h[100001], fa[100001], fath[100001], X, t;
ll Hp[100001], Ad[100001], Def[100001], Num[100001], tim[100001];
bool vis[100001];
struct node {
int to, next;
}w[200001];
void add(int x, int y)
{
w[++t] = (node){y, h[x]}; h[x] = t;
w[++t] = (node){x, h[y]}; h[y] = t;
}
void dfs(int now, int fath) {
for (int i = h[now]; i; i = w[i].next)
if (w[i].to != fath) {
fa[w[i].to] = now;
dfs(w[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)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
}
fath[1] = 1;
scanf("%lld%lld%lld", &Hp[1], &Ad[1], &Def[1]);//牛半仙的血量攻击防御
for (int i = 2; i <= n; ++i)
{
fath[i] = i;
scanf("%lld%lld%lld%lld", &Hp[i], &Ad[i], &Def[i], &Num[i]);
tim[i] = Hp[i] / (Ad[1] - Def[i]);//计算出要多少次攻击
if (Hp[i] % (Ad[1] - Def[i]) == 0) tim[i]--;
Hp[1] -= (Ad[i] - Def[1]) * tim[i];
Q.push(make_pair(1.0 * Num[i] / tim[i], i));
}
dfs(1, 0);
vis[1] = 1;
while (!Q.empty()) {
int now = Q.top().second;
Q.pop();
if (vis[now]) continue;
vis[now] = 1;
X = find(fa[now]);
Hp[1] += tim[now] * Num[X];//补回前面没算防御扣的血
tim[X] += tim[now];
Num[X] += Num[now];
if (!vis[X]) Q.push(make_pair(1.0 * Num[X] / tim[X], X));
fath[now] = X;
}
printf("%lld", Hp[1]);
}