您的位置:首页 > 其它

bzoj4919: 大根堆

2017-07-05 18:34 411 查看

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
,fa
,q
,ql=0,qr=0,deg
;
std::multiset<int>vs
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: