洛谷 P5290 [十二省联考2019]春节十二响 堆+启发式合并

本文详细解析了洛谷P5290题目的解题思路,通过分析一条链且根不为链端的情况,提出了一种有效的解决策略。文章探讨了如何在任意子树中合并两个儿子的方案,避免选择两个集合都在一边导致的冲突,并使用堆来维护大小关系,最终实现了最优匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
https://www.luogu.org/problemnew/show/P5290

分析:
考虑一条链且根不为链端的情况。一定是根左儿子的一个数和右儿子的一个数组成一个集合。
因为显然两个节点都在一侧显然不行,只选一个太亏。
那么就是左边最大匹配右边最大,左边第二匹配右边第二……
拓展到任意子树,我们可以合并两个儿子的方案。
显然不能选两个集合都在一边(一定会有冲突,不然这两个集合一定会合并成一个集合)。
那么还是左儿子最大配上右儿子最大……,多个儿子依次合并。根节点独自开一个集合。
每次用小集合加入到大集合中,可以用堆维护大小关系。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#define LL long long

const int maxn=2e5+7;

using namespace std;

int n,x,cnt,top;
int a[maxn],ls[maxn],id[maxn],b[maxn];
LL ans;

struct edge{
    int y,next;
}g[maxn];

priority_queue <int> q[maxn];

void add(int x,int y)
{
    g[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}

void dfs(int x)
{
    id[x]=++cnt;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        dfs(y);
        if (q[id[x]].size()<q[id[y]].size()) swap(id[x],id[y]);
        top=0;
        while (!q[id[y]].empty())
        {
            b[++top]=max(q[id[x]].top(),q[id[y]].top());
            q[id[x]].pop(),q[id[y]].pop();
        }
        for (int j=1;j<=top;j++) q[id[x]].push(b[j]);
    }
    q[id[x]].push(a[x]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        add(x,i);
    }
    cnt=0;
    dfs(1);
    ans=0;
    while (!q[id[1]].empty())
    {
    	ans+=(LL)q[id[1]].top();
    	q[id[1]].pop();
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值