[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());
}
阅读更多
版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/74906097
个人分类: 平衡树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭