ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

Description

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

Input

第一行包含一个正整数n(1<=n<=200000),表示节点的个数。
接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9,1<=p_i<i,p_1=0),表示每个节点的权值与父亲。

Output

输出一行一个正整数,即最多的点数。
平衡树启发式合并,维护当前子树内选k个点,选出的点权最大值的最小值
#include<bits/stdc++.h>
const int N=200007;
char buf[70007],*ptr=buf+70000;
int G(){
    if(ptr-buf==70000)fread(ptr=buf,1,70000,stdin);
    return *ptr++;
}
int _(){
    int x=0;
    if(ptr-buf<69900){
        while(*ptr<48)++ptr;
        while(*ptr>47)x=x*10+*ptr++-48;
    }else{
        int c=G();
        while(c<48)c=G();
        while(c>47)x=x*10+c-48,c=G();
    }
    return x;
}
int n;
int v[N],fa[N],q[N],ql=0,qr=0,deg[N];
std::multiset<int>vs[N];
typedef std::multiset<int>::iterator mit;
int main(){
    n=_();
    for(int i=1;i<=n;++i){
        v[i]=_();
        ++deg[fa[i]=_()];
    }
    for(int i=1;i<=n;++i)if(!deg[i])q[++qr]=i;
    while(ql!=qr){
        int w=q[++ql],f=fa[w];
        mit u=vs[w].lower_bound(v[w]);
        if(u==vs[w].end())vs[w].insert(v[w]);
        else *const_cast<int*>(&*u)=v[w];
        if(f){
            if(vs[f].size()<vs[w].size())std::swap(vs[f],vs[w]);
            for(mit it=vs[w].begin(),e=vs[w].end();it!=e;++it)vs[f].insert(*it);
            vs[w].clear();
            if(!--deg[f])q[++qr]=f;
        }
    }
    printf("%d\n",vs[1].size());
    return 0;
}

 

posted on 2017-07-05 18:34  nul  阅读(1024)  评论(0编辑  收藏  举报