题意:
分析:
- 暴力
对于 (n<=6) 的点我们可以暴力枚举集合,插板法
对于链的情况,我们将 (1) 节点左右分为两条链,把两条链的的点值放进两个堆里面,每一次将堆顶的元素取 (max)
- 正解
我们按照暴力的思路继续考虑,我们发现,对于一个节点,它的两个儿子的关系和上面暴力中的两个链的关系一样,这就给了我们启发,我们可以每次将两个儿子节点合并成一个节点,这样不断合并下去就会形成一条链,而链的处理就和上面的暴力一模一样
那么问题就转化成了按照什么样的标准和要求合并两个儿子节点 ?
首先我们先来证明几个结论:
- 节点数少的答案不会比节点数大的答案劣
证明:我们将一个节点拆成两个节点以后,最大值不仅不变,还会多出一个新的值,显然不优
- 整棵树的划分段数最少为最深的叶子的深度
证明:因为最深的叶子到根的路径上的任意两个点都不能划分到同一段里面,所以段数至少为最深的叶子的深度
好,有了上面的结论我们可以得到合并的规律了,我们每次都要把短的那条链向长的链上合并,且一个左链上的点只能与最多一个右链上的点合并成为新的链上的一个点,值的大小为两个元素的 (max)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 2e5+5;
long long ans=0;
int head[maxn],w[maxn],id[maxn],tmp[maxn];
int n,cnt=0,idx;
priority_queue<int> q[maxn];
struct edge
{
int to,nxt;
}e[maxn<<1];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int u,int fa)
{
id[u]=++idx;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
if(q[id[v]].size()>q[id[u]].size()) swap(id[u],id[v]);
int tot=q[id[v]].size();
for(int j=1;j<=tot;j++)
{
tmp[j]=max(q[id[u]].top(),q[id[v]].top());
q[id[u]].pop();q[id[v]].pop();
}
for(int j=1;j<=tot;j++) q[id[u]].push(tmp[j]);
}
q[id[u]].push(w[u]);
}
void work()
{
int a;
n=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=2;i<=n;i++) a=read(),add(a,i),add(i,a);
dfs(1,0);
while(q[id[1]].size()) ans+=q[id[1]].top(),q[id[1]].pop();
printf("%lld
",ans);
}
}
int main()
{
zzc::work();
return 0;
}