题解:没有上司的舞会

题目传送门

树型 DP

\(dp_{x,i}\)\(i\in\{0,1\}\))表示编号为 \(x\) 的职员来或不来参加舞会的情况下,其子树内的最大快乐指数。

那么,初始时就有 \(dp_{x,0}=0,dp_{x,1}=r_x\),即只算其本身的贡献

对于节点 \(x\) 的子节点 \(y_1,y_2,y_3,\cdots,y_k\),该如何转移呢?

先考虑 \(dp_{x,1}\),因为上司已经参加,因此下属(即子节点)都不应当参加,所以就有:

\[dp_{x,1}\leftarrow dp_{x,1}+\sum_{i=1}^kdp_{y_i,0} \]

那么 \(dp_{x,0}\) 呢?

上司没有参加,则下属可参加可不参加

因此:

\[dp_{x,0}\leftarrow dp_{x,0}+\sum_{i=1}^k\max(dp_{y_i,0},dp_{y_i,1}) \]

总结一下就是,对于节点 \(x\) 和其子节点 \(y_1,y_2,y_3,\cdots,y_k\)

\[dp_{x,1} = r_x+\sum_{i=1}^kdp_{y_i,0}\\ dp_{x,0} = \sum_{i=1}^k\max\left(dp_{y_i,0},dp_{y_i,1}\right)\\ \]


然后需要找到整棵树的根节点,可以随便找一个点 \(\mathcal O(n)\) 跳一遍即可。(建图时使用有向边)

然后 \(\mathcal O(n)\) 递推,总时间复杂度:\(\mathcal O(n)\)

注意答案有可能小于 \(0\)

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=6e3;
struct graph{
	struct edge{
		int v,r;
	}a[2*(N-1)+1];
	
	int h[N+1],f[N+1];
	void create(int u,int v){
		static int top;
		a[++top]={v,h[u]};
		h[u]=top;
	}
}g;
int n,r[N+1],dp[N+1][2];
void dfs(int x){
	dp[x][0]=0;
	dp[x][1]=r[x];
	for(int i=g.h[x];i>0;i=g.a[i].r){
		dfs(g.a[i].v);
		dp[x][0]+=max(dp[g.a[i].v][0],dp[g.a[i].v][1]);
		dp[x][1]+=dp[g.a[i].v][0];
	}
}
int root(){
	int p=1;
	while(g.f[p]){
		p=g.f[p];
	}return p;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",r+i);
	}
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		g.create(v,u);
		g.f[u]=v;
	}
	int Max=-2147483647;
	dfs(root());
	for(int i=1;i<=n;i++){
		Max=max(Max,dp[i][0]);
		Max=max(Max,dp[i][1]);
	}
	printf("%d\n",Max);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-20 23:21  TH911  阅读(7)  评论(0)    收藏  举报