Problem
将给定的权值放在树的叶子结点上,每个非叶子结点的值为权值最大的儿子,不计根,求最大总权值。
Solution
贪心即可,每次从优先队列弹出来的点看看其深度是不是真实深度,不是的话再加进去,这个样每个点记过贡献的点其实只会被遍历一次,时间复杂度O(n)。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e5+5;
int n,k,cnt,last[maxn],a[maxn],dep[maxn],f[maxn],num;
bool isleave[maxn],vis[maxn];
bool cmp(const int a,const int b)
{
return a>b;
}
struct edge
{
int v,next;
} e[maxn*4];
struct leaves
{
int u,dis;
bool operator<(const leaves & le)const
{
return dis<le.dis;
}
};
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=last[u];
last[u]=cnt;
}
void dfs(int u,int fa)
{
for(int i=last[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
isleave[u]=1;
dep[v]=dep[u]+1;
dfs(v,u);
}
}
priority_queue<leaves>q;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=k; i++)
scanf("%d",&a[i]);
sort(a+1,a+1+k,cmp);
for(int i=1,u; i<=n-1; i++)
{
scanf("%d",&u);
add(u,i);
add(i,u);
f[i]=u;
}
dfs(0,-1);
for(int i=1; i<=n-1; i++)
{
if(!isleave[i])
q.push({i,dep[i]});
}
long long ans=0;
while(!q.empty())
{
leaves lef=q.top();
q.pop();
vis[lef.u]=1;
int tmp=1;
int x=lef.u;
while(!vis[f[x]]&&f[x])
{
x=f[x];
tmp++;
}
if(tmp==lef.dis)
{
ans+=(long long)a[++num]*lef.dis;
x=lef.u;
while(!vis[f[x]]&&f[x])
{
vis[f[x]]=1;
x=f[x];
}
}
else
q.push({lef.u,tmp});
}
cout<<ans<<endl;
return 0;
}