1. 程式人生 > 實用技巧 >2020牛客多校第九場B- Groundhog and Apple Tree

2020牛客多校第九場B- Groundhog and Apple Tree

https://ac.nowcoder.com/acm/contest/5674/B

題意

現在有一棵樹,你要從1開始跳一遍所有的點並且每條邊只能走兩次,再回到1,每條邊都有一個邊權,你走過這條邊會先消耗\(w_i\)點HP,每個點都有一個果子,吃掉這個果子會上升\(a_i\)點HP,你在任何時候的HP不能小於0.並且你如果休息一秒鐘會恢復1點HP。問你最少要休息多少時間才能走完這棵樹。

題解

\(f[u]\)為遍歷完子樹增加多少體力,\(g[u]\)表示體力為0遍歷完子樹最低減少到多少

那麼最後答案顯然為\(min(0, -g[1])\)

考慮這張圖,綠線左邊的是增加體力為正的情況,顯然要g大的的排在前面,這樣保證最低點最高,對於綠線右邊,可以想到,我們需要的是最低點低的,且增加體力相對多的排在前面,所以按f-g排序,按樹形dp轉移即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
    inline char read() {
    #ifdef _WIN32
        return getchar();
    #endif
        static const int IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }
    template <typename _Tp> inline READ & operator >> (_Tp&x) {
        static char c11, boo;
        for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
            if(c11 == -1) return *this;
            boo |= c11 == '-';
        }
        for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
        boo && (x = -x);
        return *this;
    }
} in;

const int N = 1e5 + 50;
int a[N];
struct node {
    int v, w;
    node(int v = 0, int w = 0): v(v), w(w) {}
};
vector<node> G[N];
ll f[N], g[N];//f遍歷完子樹增加多少體力,g體力為0遍歷完子樹最低減少到多少
struct S1 {
    ll x, y;
    bool operator < (const S1 &b) const {
        return y > b.y;
    } 
};
struct S2 {
    ll x, y;
    bool operator < (const S2 &b) const {
        return x - y > b.x - b.y;
    }
};

void dfs(int u, int fa) {
    for (node e : G[u]) {
        if (e.v == fa) continue;
        dfs(e.v, u);
    }
    vector<S1> s1; vector<S2> s2;
    for (node e : G[u]) {
        if (e.v == fa) continue;
        f[e.v] -= 2ll * e.w;
        g[e.v] -= e.w;
        g[e.v] = min(g[e.v], (ll)-e.w);
        g[e.v] = min(g[e.v], f[e.v]);
        if (f[e.v] >= 0) s1.push_back({f[e.v], g[e.v]});
        else s2.push_back({f[e.v], g[e.v]});
    }
    sort(s1.begin(), s1.end()); sort(s2.begin(), s2.end());
    for (auto e : s1) {
        g[u] = min(g[u], e.y + f[u]);
        f[u] += e.x;
    }
    for (auto e : s2) {
        g[u] = min(g[u], e.y + f[u]);
        f[u] += e.x;
    }
    f[u] += a[u]; g[u] += a[u];
}
void solve() {
    int n; in >> n;
    for (int i = 1; i <= n; i++) in >> a[i], G[i].clear(), f[i] = g[i] = 0;
    for (int i = 1; i < n; i++) {
        int u, v, w; in >> u >> v >> w;
        G[u].push_back(node(v, w));
        G[v].push_back(node(u, w));
    }
    dfs(1, 0);
    printf("%lld\n", max(0ll, -g[1]));
}
int main() {
    int t; in >> t;
    while (t--) solve();
    return 0;
}