【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章