前言
这是我打酱油之旅分最高的题
但也就75pts
题目
讲解
考虑维护每个子树的段
我们只需合并每个子树的段
到父节点,最后统计根中所有的段的大小
每个子树当然最多只能选出一个点与兄弟节点的一个点合并
贪心地想,只需要将每个子树最大的,次大的,...合并起来就好了
但是这里需要一个小技巧,当合并两个段
的时候,需要使用启发式合并保证复杂度
众所周知,用启发式合并可以把(O(n^2))优化成(O(nlog_2n))
代码
//12252024832524
#include <queue>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 200005;
int n;
int a[MAXN];
priority_queue<int> q[MAXN];
LL ans;
int Read()
{
int x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
void Put1(LL x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
void Put(LL x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x);
if(c >= 0) putchar(c);
}
template <typename T>T Max(T x,T y){return x > y ? x : y;}
template <typename T>T Min(T x,T y){return x < y ? x : y;}
template <typename T>T Abs(T x){return x < 0 ? -x : x;}
int head[MAXN],tot;
struct edge
{
int v,nxt;
}e[MAXN];
void Add_Edge(int x,int y)
{
e[++tot].v = y;
e[tot].nxt = head[x];
head[x] = tot;
}
void mege(int x,int y)
{
priority_queue<int> dz;
if(q[x].size() < q[y].size()) swap(q[x],q[y]);
while(!q[y].empty())
{
dz.push(Max(q[x].top(),q[y].top()));
q[x].pop();q[y].pop();
}
while(!dz.empty()) q[x].push(dz.top()),dz.pop();
}
void dfs(int x)
{
for(int i = head[x]; i ;i = e[i].nxt)
dfs(e[i].v),mege(x,e[i].v);
q[x].push(a[x]);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
for(int i = 1;i <= n;++ i) a[i] = Read();
for(int i = 2;i <= n;++ i) Add_Edge(Read(),i);
dfs(1);
while(!q[1].empty()) ans += q[1].top(),q[1].pop();
Put(ans);
return 0;
}