思维难度不大,在考上上写的启发式合并写错了,只拿了 60 pts,好难过QAQ
没什么太难的,在考场上想出链的部分分之后很容易就能想到正解.
没错,就是非常短的启发式合并.
注意一下,写的要漂亮一点,否则会疯狂 TLE.
还有一个细节,想交换优先队列时不能直接交换队列,而是对于树中每个节点都存一个在实际队列编号中的位置,计为 $idx[u]$,每次交换 $idx[u]$ 和 $idx[to[v]]$.
Code:
#include <cstdio> #include <algorithm> #include <cstring> #include <set> #include <queue> #define maxn 200001 #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; priority_queue<int>Q[maxn]; int edges; int n; int head[maxn],to[maxn],nex[maxn],val[maxn],f[maxn]; void addedge(int u,int v){ nex[++edges] = head[u], head[u] = edges, to[edges] = v; } int tmp[maxn]; int id[maxn]; int idx; void DFS(int u){ id[u]=++idx; for(int v=head[u];v;v=nex[v]){ DFS(to[v]); if(Q[id[u]].size() < Q[id[to[v]]].size()) swap(id[u],id[to[v]]); int m=Q[id[to[v]]].size(); for(int i=1;i<=m;++i) { tmp[i]=max(Q[id[u]].top(),Q[id[to[v]]].top()); Q[id[u]].pop(); Q[id[to[v]]].pop(); } for(int i=1;i<=m;++i) Q[id[u]].push(tmp[i]); } Q[id[u]].push(val[u]); } int main(){ //setIO("spring"); scanf("%d",&n); for(int i = 1;i <= n; ++i) scanf("%d",&val[i]); for(int i = 2;i <= n; ++i) scanf("%d",&f[i]); for(int i = 2;i <= n; ++i) addedge(f[i],i); DFS(1); long long ans = 0; while(!Q[id[1]].empty())ans += (long long)Q[id[1]].top(), Q[id[1]].pop(); printf("%lld ",ans); return 0; }