没有上司的舞会

题目传送门

【思路分析】:

首先我们可以由题意可知,这是一棵树,同时如果作为儿子节点去了舞会,那就说明父亲节点是不会去的;

【状态设计】:

作为一名职员是否愿意参加只跟他的直接上司有关系,所以我们在设状态的时候就可以多开一维来表示是否参加,也就是说树上的节点存储两个信息,第一个就是该职员去参加舞会的最大值,第二个就是该职员不去参加舞会的最大值。所以状态也就是 \(f_{i,1}\)表示该节点的员工去参加的最大值,\(f_{i,0}\)表示不去;

状态转移:

 \(f_{i,0}=\)\(\sum\limits_{to \in son(i)} max(f_{to,0},f_{to,1})\)意为这个不去的时候,他的儿子有两个选择,分别对应着去或者不去,取\(Max\)
\(f_{i,1}=r_i+\sum\limits_{to \in son(i)} f_{to,0}\)意为这个节点去的话,那么他的所有儿子都不能去,他的快乐值和他儿子不能去的最大值的 \(\sum\)即为\(ans\)

【细节问题】:

首先我们肯定是建树,在建树的时候我们要记录一下每一个节点是否有父亲,然后求出根节点,因为题目没保证说是节点\(1\)就是校长,同时建树的时候建的树为一个反向有向图,题目给定\(A \to 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;
} 
posted @ 2020-11-18 22:21  SkyFairy  阅读(77)  评论(0编辑  收藏  举报