[长链剖分][优先队列] LibreOJ #3052 春节十二响

题目大意

  • 给一棵树,点有点权,要求把所有节点分成若干个集合,使得同一集合中任意两点不存在祖先关系,且每一集合的最大点权的和最小

题解

  • 考虑递归处理,假设当前节点的两棵子树都已处理好,那么合并两棵子树的方式为最大值和最大值合并,次大值和次大值合并
  • 可以用长链剖分+优先队列来优化到O(nlogn)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <queue>
 5 using namespace std;
 6 const int N=200010;
 7 int n,tot,cnt,sz,a[N],head[N],id[N],son[N],deep[N],tmp[N];
 8 struct edge{int to,from;}e[N];
 9 priority_queue<int>Q[N];
10 long long ans;
11 void insert(int x,int y){ e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; }
12 void pre(int x)
13 {
14     deep[x]=1;
15     for (int i=head[x];i;i=e[i].from) pre(e[i].to),deep[x]=max(deep[x],deep[e[i].to]+1);
16     for (int i=head[x];i;i=e[i].from) if (deep[x]==deep[e[i].to]+1) son[x]=e[i].to;
17 }
18 void dfs(int x)
19 {
20     if (son[x]) dfs(son[x]),id[x]=id[son[x]];
21     for (int i=head[x];i;i=e[i].from) 
22         if (e[i].to!=son[x])
23         {
24             dfs(e[i].to),tot=0;
25             while (!Q[id[e[i].to]].empty()) tmp[++tot]=max(Q[id[x]].top(),Q[id[e[i].to]].top()),Q[id[x]].pop(),Q[id[e[i].to]].pop();
26             while (tot) Q[id[x]].push(tmp[tot]),tot--;
27         }
28     if (!id[x]) id[x]=++sz;
29     Q[id[x]].push(a[x]);
30 }
31 int main()
32 {
33     scanf("%d",&n);
34     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
35     for (int i=2,x;i<=n;i++) scanf("%d",&x),insert(x,i);
36     pre(1),dfs(1);
37     while (!Q[id[1]].empty()) ans+=Q[id[1]].top(),Q[id[1]].pop();
38     printf("%lld",ans);
39 }
原文地址:https://www.cnblogs.com/Comfortable/p/11193343.html