1. 程式人生 > >bzoj 1040: [ZJOI2008]騎士(樹形DP)

bzoj 1040: [ZJOI2008]騎士(樹形DP)

 Z國的騎士團是一個很有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各 界的讚揚。最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百里,在和平環境 中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。於是人們把所有的希望都寄託在了騎士團的身上,就像期待有一 個真龍天子的降生,帶領正義打敗邪惡。騎士團是肯定具有打敗邪惡勢力的能力的,但是騎士們互相之間往往有一 些矛盾。每個騎士都有且僅有一個自己最厭惡的騎士(當然不是他自己),他是絕對不會與自己最厭惡的人一同出 徵的。戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱鉅的任務,從所有 的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的 情況),並且,使得這支騎士軍團最具有戰鬥力。為了描述戰鬥力,我們將騎士按照1至N編號,給每名騎士一個戰 鬥力的估計,一個軍團的戰鬥力為所有騎士的戰鬥力總和。

這是一個樹,加上一條邊。

樹上相鄰的點不能選。

所以我們就可以把樹上的環破壞。

找到環,去掉一條邊。記錄這條邊的兩個節點,

假設分別是 U V,

我們可以分別以 U V 為根節點。

U 不選,查詢可以得到的最大值,

V不選,查詢可以得到的最大值。

兩個最大值,去max。

f[x] 代表選擇 x 點的子樹最大值。,

g[x] 代表不選x 點的子樹最大值。

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const int N = 3e6+10;
int Head[N],Next[N],to[N],cnt = -1; 
LL f[N],g[N],val[N];
bool vis[N];
int U,V,E;
void Add(int u, int v){
	Next[++cnt] = Head[u];
	Head[u] = cnt;
	to[cnt] = v;
	Next[++cnt] = Head[v];
	Head[v] = cnt;
	to[cnt] = u;
}
void dfs(int u, int fa){
	vis[u] = 1;
	for (int i = Head[u]; i != -1; i = Next[i]){
		int v = to[i];
		if (v == fa) continue;
		if (vis[v]){
			U = u;
			V = v;
			E = i;
			continue;
		}
		dfs(v,u);
	}
	return;
}
void solve(int u, int fa){
	f[u] = val[u]; g[u] = 0;
	for (int i = Head[u]; i != -1; i = Next[i]){
		int v = to[i];
		if ((i==E)||(v == fa)||((i^1) == E)) continue;
		solve(v,u);
		f[u] += g[v];
		g[u] += max(f[v],g[v]);
	}
}
int main(){
	int n;
	scanf("%d",&n);
	mem(Head,-1);
	go(i,1,n){
		int x,y;
		scanf("%d%d",&x,&y);
		val[i] = x;
		Add(i,y);
	}
	LL ans = 0;
	go(i,1,n)
	if (!vis[i]){
		dfs(i,0);
		solve(U,0);
		LL temp = g[U];
		solve(V,0);
		temp = max(temp,g[V]);
		ans += temp;
	}
	cout<<ans<<endl;
	return 0;
}