正题
题目链接:https://www.luogu.org/problemnew/show/P5290
题目大意
将一棵树的所有节点分城若干个组。每个组的价格是这个组中价格最大的点,要求这个组中没有任何祖孙关系。
求所有组的权值和最小。(1号为根节点)
解题思路
我们先想10,11,1210,11,1210,11,12的点(1号节点最多有两条支链)。
我们可以将两条链分成两个组,然后每次取两个组中最大的两个进行合并。
那如果有多条支链但是这些支链不重合呢?那么我们可以将前两个合并好之后继续用现在的来合并后面的,因为这样一定不会重合。
那如果支链重合呢?那么我们假设一个节点的所有子节点都合并好了,那么我们是不是就可以用上面的方法得出这个点,然后继续用来求父节点。
我们可以将每个点开一个堆,然后从子节点合并给父节点。时间复杂度O(n2logn)O(n^2\ log\ n)O(n2 log n)显然无法胜任本题,但我们可以每次合并的时候将小的堆合并到大的堆(启发式合并),那么时间就会被优化到O(nlog2n)O(n\ log^2\ n)O(n log2 n)(每个点最多被合并lognlog\ nlog n次)
codecodecode
#include<cstdio>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e5+10;
struct node{ll to,next;
}a[N];
ll ls[N],tot,ans,w[N],id[N],cnt,tmp[N],n;
priority_queue<ll> d[N];
void addl(ll x,ll y)
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;
}
void count_ans(ll x)
{id[x]=++cnt;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;count_ans(y);if(d[id[y]].size()>d[id[x]].size()) swap(id[x],id[y]);ll len=d[id[y]].size();for(ll i=1;i<=len;i++){tmp[i]=max(d[id[x]].top(),d[id[y]].top());d[id[x]].pop();d[id[y]].pop();}for(ll i=1;i<=len;i++)d[id[x]].push(tmp[i]);}d[id[x]].push(w[x]);
}
int main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld",&w[i]);for(ll i=2;i<=n;i++){ll x;scanf("%lld",&x);addl(x,i);}count_ans(1);while(!d[id[1]].empty())ans+=d[id[1]].top(),d[id[1]].pop();printf("%lld",ans);
}