[十二省联考]春节十二响
题面好长啊
大致题意:给定一个序列,然后给定他们树上的父子关系,我们需要将其分成若干集合,使其没有父子关系,然后求所有集合的大小之和的最小值。
我们通过没有父子关系很容易想到我们需要从下(叶子)向上考虑(证明略)
我们先考虑一条链是什么做法:我们可以开两个堆然后两边分别弹出然后取max
我们将其推广到树上,我们开n个priority_queue
让后每次取头
然后在树合并时注意进行启发式合并
#include<bits/stdc++.h> #define int long long using namespace std; const int N=2e5+7; int n,ans; int head[N],nxt[N<<1],to[N<<1]; int _; void add(int x,int y) { _++; to[_]=y; nxt[_]=head[x]; head[x]=_; return ; } int a[N],cnt; priority_queue<int >q[N]; int p[N],id[N]; void dfs(int x,int fa) { cnt++; id[x]=cnt; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==fa) continue; dfs(y,x); if(q[id[x]].size()<q[id[y]].size()) swap(id[x],id[y]); int sum=q[id[y]].size(); for(int j=1;j<=sum;j++) { p[j]=max(q[id[x]].top(),q[id[y]].top()); q[id[x]].pop(); q[id[y]].pop(); } for(int j=1;j<=sum;j++) q[id[x]].push(p[j]); } q[id[x]].push(a[x]); return ; } signed main() { ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=2;i<=n;i++) { int w; cin>>w; add(w,i); add(i,w); } dfs(1,0); while(q[id[1]].size()) { ans+=q[id[1]].top(); q[id[1]].pop(); } cout<<ans; return 0; }