Luogu5290 [十二省联考2019] 春节十二响 【贪心】【堆】

题目分析:

对于一个根,假设我们对每个子树分别求出了一种答案,那么怎么合并答案是最小的呢?

首先考虑这些答案里面最大的那个数字,它肯定要融合其它组里面的最大数字。以此类推

所以最好的合并方式是,每个子树的答案从大到小排序,然后依次合并。

然后我们会发现,这个其实是可以划分子问题的,因为如果某个子树不按照这种方式划分,那么必然不会使得某个位置变小另一个位置变大,只会使得相应位置变大。

所以按子树划分下去,把堆合并就行。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 202000;
 5 
 6 int n;
 7 int a[maxn],f[maxn];
 8 vector <int> g[maxn];
 9 int pts[maxn],num;
10 priority_queue<int,vector<int>,less<int> > pq[maxn],hc;
11 
12 void read(){
13     scanf("%d",&n);
14     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
15     for(int i=2;i<=n;i++) scanf("%d",&f[i]),g[f[i]].push_back(i); 
16 }
17 
18 void dfs(int now){
19     for(int i=0;i<g[now].size();i++) {
20     dfs(g[now][i]);
21     if(pq[pts[now]].size() < pq[pts[g[now][i]]].size())
22         pts[now] = pts[g[now][i]];
23     }
24     for(int i=0;i<g[now].size();i++){
25     if(pts[now] != pts[g[now][i]]){
26         while(pq[pts[g[now][i]]].size()){
27         int k = pq[pts[now]].top(); pq[pts[now]].pop();
28         int z = pq[pts[g[now][i]]].top(); pq[pts[g[now][i]]].pop();
29         hc.push(max(k,z));
30         }
31         while(!hc.empty()){pq[pts[now]].push(hc.top());hc.pop();}
32     }
33     }
34     if(pts[now] == 0) pts[now] =++num;
35     pq[pts[now]].push(a[now]);
36 }
37 
38 int main(){
39     read();
40     dfs(1);
41     long long ans = 0;
42     while(!pq[pts[1]].empty()){
43     ans += pq[pts[1]].top();
44     pq[pts[1]].pop();
45     }
46     printf("%lld\n",ans);
47     return 0;
48 }

 

posted @ 2019-06-05 16:28  menhera  阅读(158)  评论(0编辑  收藏  举报