首页 > 代码库 > bzoj4919: 大根堆

bzoj4919: 大根堆

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;}

 

bzoj4919: 大根堆