若是是一條折鏈,顯然維護兩側的值,每次兩個堆分別彈出一個\(max\)而後合併一下,最後再放回去就能夠了。
那麼如今回到一棵樹上,能夠認爲就是自己有一條鏈,如今每次要合併一條鏈進來,那麼拿一個堆維護這個合併過程就能夠了。爲了保證複雜度用啓發式合併。
在\(C++11\)下能夠直接使用\(.swap()\)函數來進行優先隊列的交換。
爲了在\(BZOJ\)上過就寫的普通的啓發式合併。ios
#include<cstdio> #include<vector> #include<queue> using namespace std; #define MAX 200200 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,M[MAX];long long ans; priority_queue<int> S[MAX]; struct Line{int v,next;}e[MAX]; int h[MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} int ID[MAX]; void dfs(int u) { ID[u]=u; for(int i=h[u];i;i=e[i].next) { int v=e[i].v;dfs(v); if(S[ID[v]].size()>S[ID[u]].size())swap(ID[u],ID[v]); vector<int> tmp; while(!S[ID[v]].empty())tmp.push_back(max(S[ID[u]].top(),S[ID[v]].top())),S[ID[u]].pop(),S[ID[v]].pop(); while(!tmp.empty())S[ID[u]].push(tmp.back()),tmp.pop_back(); } S[ID[u]].push(M[u]); } /* void dfs(int u) { for(int i=h[u];i;i=e[i].next) { int v=e[i].v;dfs(v); if(S[v].size()>S[u].size())S[u].swap(S[v]); vector<int> tmp; while(!S[v].empty())tmp.push_back(max(S[u].top(),S[v].top())),S[u].pop(),S[v].pop(); while(!tmp.empty())S[u].push(tmp.back()),tmp.pop_back(); } S[u].push(M[u]); } */ int main() { n=read(); for(int i=1;i<=n;++i)M[i]=read(); for(int i=2;i<=n;++i)Add(read(),i); dfs(1); while(!S[ID[1]].empty())ans+=S[ID[1]].top(),S[ID[1]].pop(); printf("%lld\n",ans); return 0; }