[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
输出一行一个正整数,即最多的点数。
solution
链的情况,不难发现就是LIS的长度
LIS有一个二分的做法
依照这个思路,我们将问题放在树上
将dp数组\(f\)换成一个一个multiset
当回溯到\(x\)时,将\(x\)的所有儿子的\(f_y\)加入到\(f_x\)中,然后再考虑加入一个\(v_u\)。。。
时间复杂度为\(O(nlogn)\),足以通过此题
code
void dfs(int x){
int fg=0;
for(int i=hd[x];i;i=nxt[i]){
int y=to[i];
dfs(y);
if(f[x].size()<=f[y].size()){
swap(f[y],f[x]);
}
for(auto i:f[y])f[x].insert(i);
fg=1;
f[y].clear();
}
if(!fg)f[x].insert(a[x]);
else{
auto y=f[x].lower_bound(a[x]);
if(y!=f[x].end())f[x].erase(y);
f[x].insert(a[x]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
a[i]=x;
if(y)add(y,i);
}
dfs(1);
printf("%d",(int)f[1].size());
return 0;
}
完结撒花❀
★,°:.☆( ̄▽ ̄)/$:.°★ 。
标签:大根堆,BZOJ4919,一个,int,解题,LIS,节点 From: https://www.cnblogs.com/Aurora1217/p/16647892.html