[启发式合并][堆]luogu P5290 [十二省联考2019]春节十二响

题面

https://www.luogu.com.cn/problem/P5290

分析

容易发现一个子树内部的选择不会影响他的兄弟子树,考虑用堆来记录当前子树的若干内存段

显然,如果跨子树便可以选择一个与当前子树的某内存段结合。而且因为取max,所以选最大的两个结合最优

合并堆的时候启发式合并即可

代码

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int N=2e5+10;
struct Graph {
    int v,nx;
}g[N];
int cnt,list[N],bel[N],a[N],acnt;
int n,m[N];
ll ans;
priority_queue<int> q[N];

void Add(int u,int v) {g[++cnt]=(Graph){v,list[u]};list[u]=cnt;}

void Merge(int &x,int &y) {
    if (q[x].size()<q[y].size()) swap(x,y);acnt=0;
    while (!q[y].empty()) a[++acnt]=max(q[x].top(),q[y].top()),q[x].pop(),q[y].pop();
    for (int i=1;i<=acnt;i++) q[x].push(a[i]);
}

void DFS(int u) {
    for (int i=list[u];i;i=g[i].nx) DFS(g[i].v),Merge(bel[u],bel[g[i].v]);
    q[bel[u]].push(m[u]);
}

int main() {
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&m[i]),bel[i]=i;
    for (int i=2,u;i<=n;i++) scanf("%d",&u),Add(u,i);
    DFS(1);
    while (!q[bel[1]].empty()) ans+=q[bel[1]].top(),q[bel[1]].pop();
    printf("%lld\n",ans);
}
View Code

 

posted @ 2021-03-25 16:45  Vagari  阅读(41)  评论(0编辑  收藏  举报