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

题目:
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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值