【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;
}
发布了159 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/99972947
今日推荐