【BZOJ1040】基环树dp

BZOJ1040
每一个人会痛恨一个人,将痛恨的那个人设为父节点,显然每一个人只有一个入度。
分析(画图)可知,这个题目的模型是一个基环树森林。

我们只需要随便在基环树的环上取一个边,对边的两边的节点分别树形dp。
(0) 对于一个边,他有两个点,这两个点不能同时取。
(1) 对于一个边,一个点为x,一个点为fa[x]。
(2) 首先我们强制不选x,进行树形dp。
(3) 然后我们强制不选fa[x],再树形dp一遍。
然后比较一下,取一个最大值加起来即可。
状态转移如下:
对于一条边u->v
dp[u][0] += max(dp[v][0],dp[v][1])
dp[u][1] += dp[v][0]

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e6+7;
int fa[maxn],val[maxn],vis[maxn];
ll dp[maxn][2],ans = 0;
vector<int> G[maxn];
int root;
void dfs(int now)
{
	vis[now] = 1;
	dp[now][0] = 0,dp[now][1] = val[now];
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i];
		if(v!=root)
		{
			dfs(v);
			dp[now][0] += max(dp[v][0],dp[v][1]);
			dp[now][1] += dp[v][0];
		}
		else dp[root][1] = -INF;
	}
}
void findc(int x)
{
	vis[x] = 1;
	root = x;
	while(!vis[fa[root]])
	{
		root = fa[root];
		vis[root] = 1;
	}
	dfs(root);
	long long tmp = max(dp[root][0],dp[root][1]);
	root = fa[root];
	dfs(root);
	ans += max(tmp,max(dp[root][0],dp[root][1]));
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d%d",val+i,&x);
		fa[i] = x;
		G[x].push_back(i);
	}
	for(int i=1;i<=n;i++)
	{
		if(!vis[i]) findc(i);
	}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值