题意
给一棵树,点有点权,要求把所有节点分成若干个集合,使得同一集合中任意两点不存在祖先关系,且每一集合的最大点权的和最小。
n
≤
2
∗
1
0
5
n\le2*10^5
n≤2∗105
分析
考虑递归处理,假设当前节点的两棵子树都已处理好,那么合并两棵子树的方式为最大值和最大值合并,次大值和次大值合并,如此类推。
可以用长链剖分+优先队列来优化到
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
typedef long long LL;
const int N=200005;
int n,a[N],cnt,last[N],sz,id[N],son[N],dep[N],tmp[N];
struct edge{int to,next;}e[N];
std::priority_queue<int> que[N];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
}
void pre(int x)
{
dep[x]=1;
for (int i=last[x];i;i=e[i].next)
pre(e[i].to),dep[x]=std::max(dep[x],dep[e[i].to]+1);
for (int i=last[x];i;i=e[i].next)
if (dep[e[i].to]+1==dep[x]) son[x]=e[i].to;
}
void dfs(int x)
{
if (son[x])
{
dfs(son[x]);
id[x]=id[son[x]];
}
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==son[x]) continue;
dfs(e[i].to);
int tot=0;
while (!que[id[e[i].to]].empty())
{
tmp[++tot]=std::max(que[id[x]].top(),que[id[e[i].to]].top());
que[id[x]].pop();
que[id[e[i].to]].pop();
}
while (tot) que[id[x]].push(tmp[tot]),tot--;
}
if (!id[x]) id[x]=++sz;
que[id[x]].push(a[x]);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=2;i<=n;i++)
{
int x;scanf("%d",&x);
addedge(x,i);
}
pre(1);
dfs(1);
LL ans=0;
while (!que[id[1]].empty()) ans+=que[id[1]].top(),que[id[1]].pop();
printf("%lld\n",ans);
return 0;
}