树上选点 洛谷P12382题解

树上选点 P12382


从树上dp角度看,每一次只要遍历下一层中的所有结点即可。

分别记为两种状态,f[i][1]表示选择该点情况下在比该节点深度更大的点中能够得到的点权最大值。

易得状态转移方程:

if(ff[v]!=u){
	dp[u]=max(dp[u],dp[v]);
}//只有不相邻时,才能两个同时取
dp[u]=max(dp[u],dp[v]);

结果:60TLE

优化


dp[u]是下一层中除了dp[son[u]]中的最大值,所以,可以通过存储其最大值次大值,满足这两个值所对应的节点不在同一颗子树上

DFS的搜索原理是通过边寻找直接与其相连的节点进行遍历,而现在的思路是逐层计算,不妨脱离DFS,直接逐层遍历。

AC CODE

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
void read(int& x){
	char c;
	while((c=getchar())<48);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	return;
}//快读
int maxi(int x,int y){
	return x<y ? y : x;
}
int head[maxn],nxt[maxn],e[maxn];
int cnt;
void init_mp(){
	memset(head,-1,sizeof(head));
	cnt=-1;
	return;
}
void add_edge(int u,int v){
	e[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
	return;
}//链式前向星
int n;
int f[maxn],k[maxn];
vector<int> p[maxn];
int dep[maxn];
int m=0;
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	p[dep[u]].push_back(u); 
	m=maxi(m,dep[u]);
	for(int i=head[u];~i;i=nxt[i]){
		int v=e[i];
		dfs(v,u);
	}
	return;
}//计算深度
int dp[maxn];
int solve(){
	int a=0,b=0;
  /*a,b保存的是当前上层中拥有最大点权的节点编号*/
	int ans=0;
  //从上至下计算深度
	for(int i=1;i<=m;i++){
		for(int j=0;j<p[i].size();j++){
			int u=p[i][j];
			if(f[u]==a){
				dp[u]=dp[b]+k[u];
			}
			else{
				dp[u]=dp[a]+k[u];
			}//更新节点信息
			ans=maxi(ans,dp[u]);
		}
		for(int j=0;j<p[i].size();j++){
			int u=p[i][j];
			if(dp[a]<dp[u]){
				b=a,a=u;
			}
			else if(dp[b]<dp[u]){
				b=u;
			}//更新最大和次大值
		}
	}
	return ans;
}
int main(){
	read(n);
	init_mp();
	for(int i=2;i<=n;i++){
		read(f[i]);
		add_edge(f[i],i);
	}
	for(int i=1;i<=n;i++){
		read(k[i]);
	}
	dfs(1,0);
	printf("%d",solve());
	return 0;
}
posted @ 2025-05-23 21:23  huangems  阅读(18)  评论(0)    收藏  举报