[启发式合并][堆]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); }
在日渐沉没的世界里,我发现了你。