题解:没有上司的舞会

洛谷P1352

Posted by TH911 on January 22, 2025

题目传送门

树形 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 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//#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;
}