题解:没有上司的舞会
树型 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;
}

浙公网安备 33010602011771号