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 }