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