2020Nowcode多校 Round9 B.Groundhog and Apple Tree
题意
给一棵树 初始\(hp=0\) 经过一条边会掉血\(w_{i}\) 第一次到达一个点可以回血\(a_{i}\) 在一个点休息\(1s\)可以回复\(1hp\) 血不能小于\(0\)
每条边最多经过两次 求从起点经过所有点再回到起点到最小时间
题解
休息的话干脆就在起点休息够了再出发吧
在分叉路口的时候走哪个方向是个问题
对于每一棵子树 可以维护出走这个方向的最小\(hp\) 和走回来还剩下的\(hp\)
那么可以根据这两个值的大小关系把子树分成两种 一种是走回来回的血比走之前掉的血还多 还有一种...
显然可以在第一种的选一个掉血最少的开始先走 走完之后还剩一些血
对于剩下的 比较一下谁先走更优
#include <bits/stdc++.h>
#include <stdio.h>
#include <iostream>
#include <set>
#include <string.h>
#include <string>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int INF = 0x3f3f3f3f;
const int MAXN = 100005;
int n, cnt;
int a[MAXN];
struct node {
int to, nex, val;
}E[MAXN << 1];
int head[MAXN];
struct ed {
int id;
ll cost, gain;
}dp[MAXN];
bool cmp(ed A, ed B) {
if(A.gain > A.cost) {
if(B.gain > B.cost) return A.cost < B.cost;
else return true;
} else {
if(B.gain > B.cost) return false;
else return A.gain > B.gain;
}
}
void dfs(int x, int fa) {
vector<ed> comp;
for(int i = head[x]; i; i = E[i].nex) {
int v = E[i].to;
if(v == fa) continue;
dfs(v, x);
dp[v].id = v;
dp[v].cost += E[i].val;
dp[v].gain -= E[i].val;
if(dp[v].gain < 0) dp[v].cost -= dp[v].gain, dp[v].gain = 0;
comp.push_back(dp[v]);
}
sort(comp.begin(), comp.end(), cmp);
dp[x].cost = 0;
ll now = a[x];
for(int i = 0; i < comp.size(); i++) {
ed v = comp[i];
now -= v.cost;
if(now < 0) {
dp[x].cost += -now;
now = 0;
}
now += v.gain;
}
dp[x].gain = now;
}
int main() {
int T;
cin>>T;
while(T--) {
cnt = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), head[i] = 0;
for(int i = 1; i < n; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
E[++cnt].to = v; E[cnt].nex = head[u]; head[u] = cnt; E[cnt].val = w;
E[++cnt].to = u; E[cnt].nex = head[v]; head[v] = cnt; E[cnt].val = w;
}
dfs(1, 0);
printf("%lld\n", dp[1].cost);
}
return 0;
}