[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

输出一行一个正整数,即最多的点数。

solution

链的情况,不难发现就是LIS的长度

LIS有一个二分的做法

LIS的O(nlogn)算法(二分)

依照这个思路,我们将问题放在树上

将dp数组\(f\)换成一个一个multiset

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

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

posted @ 2022-09-01 21:28  _Youngxy  阅读(150)  评论(0)    收藏  举报