思路:

当几门课程没有关系的时候采用双开肯定会节约时间;我们可以考虑树,把1当作根来推。开两个数组,一个f[x]来存第x门课的时候花的最少时间;g[x]来存第x门课他的字树的总时间。每次x会有f[x]=max(f[i]),(其中i为x的子树),然后f[x]=a[x]+max(f[x],(g[x]-2f[x]+1)/2+f[x]),这是因为会有只有一个子树的情况,(g[x]-2f[x]+1)/2+f[x])这部分是双开最少用时间,加1是因为整除的原因。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,a[1000010],f[1000010],g[1000010];//f[x]来存第x门课的时候花的最少时间;g[x]来存第x门课他的字树的总时间
vector<ll>v[100010]; //v[x]用来存第x门课要在哪些课后面
void fun(ll x)
{
    for(auto i:v[x])
    {
        fun(i);
        f[x]=max(f[x],f[i]);//找最大的子树
        g[x]+=g[i];//子树和
    }
    f[x]=a[x]+max(f[x],(g[x]-2*f[x]+1)/2+f[x]);//学完第x门课的时间
    g[x]+=a[x];
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    for(int i=2;i<=n;i++)
    {
        ll k;scanf("%lld",&k);
        v[k].push_back(i);
    }
    fun(1);
    cout<<f[1];
    return 0;
}