CF533B 题解

CF533B

RabbieWjy


题目描述

公司有 n n n 个人, 1 1 1 是总裁,每个人有一个直接上司。每一个人有一个权值 a i a_i ai,要求找一个集合,使集合中所有人权值之和最大。其中每一个人的下属(直接,间接)总数都必须是偶数。输出最大权值。

1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 1 ≤ a i ≤ 1 0 5 1 \leq a_i \leq 10^5 1ai105

解题方法

树形 DP。

枚举每个节点,维护 f [ x ] [ 1 ] f[x][1] f[x][1] 代表在 x x x 为根的子树中,选取奇数个节点的最大权值和; f [ x ] [ 0 ] f[x][0] f[x][0] 代表在 x x x 为根的子树中,选取偶数个节点的最大权值和(记录选奇数个节点是因为有可能不选节点 x x x,而最后用这奇数个节点拼成偶数个节点)。

转移很显然:

f [ x ] [ 1 ] = max ⁡ j ∈ s o n [ x ] ( f [ x ] [ 1 ] + f [ u ] [ 0 ] , f [ x ] [ 0 ] + f [ u ] [ 1 ] ) f [ x ] [ 0 ] = max ⁡ j ∈ s o n [ x ] ( f [ x ] [ 1 ] + f [ u ] [ 1 ] , f [ x ] [ 0 ] + f [ u ] [ 0 ] ) f [ x ] [ 1 ] = max ⁡ ( f [ x ] [ 1 ] , f [ x ] [ 0 ] + v a l [ x ] ) f[x][1]=\max \limits _{j\in son[x]} (f[x][1]+f[u][0],f[x][0]+f[u][1])\\ f[x][0]=\max \limits _{j \in son[x]} (f[x][1]+f[u][1],f[x][0]+f[u][0])\\ f[x][1]=\max (f[x][1],f[x][0]+val[x]) f[x][1]=json[x]max(f[x][1]+f[u][0],f[x][0]+f[u][1])f[x][0]=json[x]max(f[x][1]+f[u][1],f[x][0]+f[u][0])f[x][1]=max(f[x][1],f[x][0]+val[x])

即用除以 u u u 为根的子树外的某些节点和以 u u u 为根的子树的某些节点拼起来,取最大值,或用 x x x 的后代们的某些与 x x x 拼起来,取最大值。

最后答案就是 f [ 1 ] [ 1 ] f[1][1] f[1][1]

也就是取偶数个 x x x 的后代们以及 x x x 或者不取 x x x,取奇数个 x x x 的后代们。

代码实现

注意初始化, f [ x ] [ 1 ] f[x][1] f[x][1] 初始不可能,应该初始化为 − i n f -inf inf

注意开 long long。

注意更新 f f f 数组时,记录原来是多少。

代码:

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

const ll inf = 1e18;

int n;
ll val[200010];
int to[200010],nxt[200010],head[200010],Cnt[200010],cnt;
ll f[200010][3];

void add(int x,int y)
{
	to[++ cnt] = y;
	nxt[cnt] = head[x];
	head[x] = cnt;
	Cnt[x] ++;
}

void dp(int x)
{
	if (!Cnt[x])
	{
		f[x][1] = val[x];
		return ;
	}
	f[x][0] = 0,f[x][1] = -inf;
	for (int i = head[x];i;i = nxt[i])
	{
		int u = to[i];
		dp(u);
		ll tmp1 = f[x][1],tmp0 = f[x][0];
		f[x][0] = max(f[x][0] + f[u][0],f[x][1] + f[u][1]);
		f[x][1] = max(tmp1 + f[u][0],tmp0 + f[u][1]);
	}
	f[x][1] = max(f[x][1],f[x][0] + val[x]);
}

int main()
{
	scanf("%d",&n);
	for (int i = 1;i <= n;i ++)
	{
		int x;
		scanf("%d%lld",&x,val + i);
		if (i != 1) add(x,i);
	}
	dp(1);
	printf("%lld\n",f[1][1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值