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; }
时间: 2024-12-07 10:56:16