题目传送门
【思路分析】:
首先我们可以由题意可知,这是一棵树,同时如果作为儿子节点去了舞会,那就说明父亲节点是不会去的;
【状态设计】:
作为一名职员是否愿意参加只跟他的直接上司有关系,所以我们在设状态的时候就可以多开一维来表示是否参加,也就是说树上的节点存储两个信息,第一个就是该职员去参加舞会的最大值,第二个就是该职员不去参加舞会的最大值。所以状态也就是 (f_{i,1})表示该节点的员工去参加的最大值,(f_{i,0})表示不去;
状态转移:
(f_{i,0}=)(sumlimits_{to in son(i)} max(f_{to,0},f_{to,1}))意为这个不去的时候,他的儿子有两个选择,分别对应着去或者不去,取(Max)
(f_{i,1}=r_i+sumlimits_{to in son(i)} f_{to,0})意为这个节点去的话,那么他的所有儿子都不能去,他的快乐值和他儿子不能去的最大值的 (sum)即为(ans)
【细节问题】:
首先我们肯定是建树,在建树的时候我们要记录一下每一个节点是否有父亲,然后求出根节点,因为题目没保证说是节点(1)就是校长,同时建树的时候建的树为一个反向有向图,题目给定(A o B)表示 (A)是(B)的上司,我们在更新的时候,更新的顺序是从儿子更新父亲,所以应该要反过来更新,如果是双向边,先保证不能走回去,不让将会一直更新;
给出代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1e6;
struct Edge
{
int nxt,to;
}edge[maxn<<1];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int number_edge,head[maxn<<1];
void add_edge(int from,int to)
{
number_edge++;
edge[number_edge].nxt=head[from];
edge[number_edge].to=to;
head[from]=number_edge;
}
int n;
int f[maxn][2];
int vis[maxn];
void dp(int x)
{
for(int i=head[x];i;i=edge[i].nxt)
{
int to=edge[i].to;
dp(to);
f[x][0]+=max(f[to][0],f[to][1]);
f[x][1]+=f[to][0];
}
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
f[i][1]=read();
}
for(int i=1;i<=n-1;i++)
{
int x=read(),y=read();
vis[x]=1;
add_edge(y,x);
}
int root;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
root=i;
break;
}
}
dp(root);
printf("%d",max(f[root][0],f[root][1]));
return 0;
}