[十二省联考]春节十二响

题面好长啊

大致题意:给定一个序列,然后给定他们树上的父子关系,我们需要将其分成若干集合,使其没有父子关系,然后求所有集合的大小之和的最小值。

 

我们通过没有父子关系很容易想到我们需要从下(叶子)向上考虑(证明略)

我们先考虑一条链是什么做法:我们可以开两个堆然后两边分别弹出然后取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;
}

 

posted @ 2021-08-06 15:09  Hehe_0  阅读(155)  评论(0)    收藏  举报