4919: [Lydsy1706月赛]大根堆 multiset 启发式合并 思路

本文介绍了一种解决树形结构大根堆问题的方法。通过维护每个节点的multiset来求解最长上升子序列,利用启发式合并策略优化计算过程。最终实现高效地选取满足条件的节点集合。

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

Description
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

题解:

好题。考虑一条链的情况,就是求自底向上求最长上升子序列,其实这种思路也可以推广到树上,具体的就是每个节点用一个multiset维护求最长上升子序列时的ff数组,f[i]的定义为选了ii个点,最大的点最小是多少,然后对于每个节点用启发式合并把儿子的multiset合并起来,最后加入自己的即可。最后根节点的size即为答案。

代码:

#include<cstdio>
#include<cstring>
#include<set>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int Maxn=200010;
struct Node{int x,id;}b[Maxn];
bool cmp(Node a,Node b){return a.x<b.x;}
int n,val[Maxn];
struct Edge{int y,next;}e[Maxn];
int last[Maxn],len=0;
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
int cnt=0;
multiset<int>S[Maxn];
void dfs(int x)
{
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        dfs(y);
        if(S[y].size()>S[x].size())swap(S[x],S[y]);
        for(set<int>::iterator j=S[y].begin();j!=S[y].end();j++)
        S[x].insert(*j);
    }
    if(S[x].size()>0&&S[x].lower_bound(val[x])!=S[x].end())S[x].erase(S[x].lower_bound(val[x]));
    S[x].insert(val[x]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int fa;
        scanf("%d%d",&b[i].x,&fa);b[i].id=i;
        ins(fa,i);
    }
    sort(b+1,b+1+n,cmp);
    b[0].x=-1;
    for(int i=1;i<=n;i++)val[b[i].id]=(cnt+=((b[i].x==b[i-1].x)?0:1));
    dfs(1);
    printf("%d",S[1].size());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值