[bzoj4919]大根堆

题目描述

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

解法

(不)容易看出这是一个变种LIS。
如果只有一条链就是LIS。
如果多条链但是最终选的点是一条链也是个树版LIS。
然后感受一下这是个LIS。
LIS有一个经典二分做法。
现在我们用set来保存二分做法的那个数组。
不同子树之间用set来启发式合并。
然后把这个点权值丢进去替换掉第一个>=它的。
最后输出set的大小。

#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
multiset<int> s[maxn];
int a[maxn],h[maxn],go[maxn],next[maxn];
int i,j,k,l,t,n,m,tot;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void merge(int x,int y){
    if (s[x].size()>s[y].size()) swap(s[x],s[y]);
    multiset<int>::iterator it=s[x].begin();
    while (it!=s[x].end()){
        s[y].insert(*it);
        it++;
    }
    s[x].clear();
}
void dfs(int x){
    int t=h[x];
    while (t){
        dfs(go[t]);
        merge(go[t],x);
        t=next[t];
    }
    multiset<int>::iterator it=s[x].lower_bound(a[x]);
    if (it!=s[x].end()) s[x].erase(it);
    s[x].insert(a[x]);
}
int main(){
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d%d",&a[i],&j);
        if (j) add(j,i);
    }
    dfs(1);
    printf("%d\n",s[1].size());
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值