题目链接:https://codeforces.com/problemset/problem/1997/D
这道题刚开始想从叶子节点开始向根方向搜索,很难写,状态不好存,遂看了几篇题解,选择了树形dp的做法。
参考:https://www.cnblogs.com/zsc985246/p/18333684
用链式前向星存下这棵树,从根节点开始深搜,这样保证在访问父节点时他的子节点全部访问过了。用一个数组u存下访问每个节点时他的所有子节点中的最小值mn是多少,在搜到叶子节点时将u赋值为它自己的值;中间节点时若v>mn,将u赋值为mn,若v<mn,将u赋值为(v+mn)/2;到根节点可以简单粗暴直接加上mn作为答案。

#include <bits/stdc++.h>
using namespace std;
int v[200005];
int u[200005];
int n;
//链式前向星
const int MAXN = 2e5 + 7;
int head[MAXN];
int cnt = 1;
struct edge {
    int to, nxt;
};
edge a[MAXN];
void add_edge(int u, int v) {
    //a[cnt].w = w;
    a[cnt].to = v;
    a[cnt].nxt = head[u];
    head[u] = cnt++;
}

void dfs(int x){
    ll mn = 1e9+7;
    for (int j = head[x]; j; j = a[j].nxt) {
        dfs(a[j].to);
        mn = min(mn, (ll)u[a[j].to]);
    }
    if (mn == 1e9+7) {
        u[x] = v[x];
        return;
    }
    if (x == 1) {
        v[x] += mn;
        return;
    }
    if (v[x] < mn) {
        u[x] = (v[x] + mn) / 2;
    }
    else {
        u[x] = mn;
    }
}

void init() {
    for (int i = 0; i <= n; i++) {
        head[i] = 0;
        v[i] = 0;
        u[i] = 0;
        cnt = 1;
        a[i].nxt = 0;
        a[i].to = 0;
    }
}

void solve() {  
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
    }
    int x;
    for (int i = 2; i <= n; i++) {
        cin >> x;
        add_edge(x, i);
    }

    dfs(1);
    cout << v[1] << '\n';

    //cnt = 1;
    init();

}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();

    return 0;
}