【HYSBZOJ】【樹形DP】3677 [Apio2014]連珠線

HYSBZOJ 3677 [Apio2014]連珠線

題目大意

◇題目傳送門◆

BZOJ 上的題面太毒瘤了,我來重寫一下

給定NN個珠子,現在可以進行如下操作:

  • Append(w,v):將一個新的珠子ww用紅線與一個已經添加了的珠子vv連起來;
  • Insert(w,u,v):將一個新的珠子插入到用紅線連起來的珠子u,vu,v,即斷開u,vu,v的紅線,並用藍線連接w,vw,vw,uw,u

給出最後的狀態(保證是一棵樹),即珠子的連接狀態和線的長度,現在要求最大化藍線的總長度。

分析

考慮最後的狀態下藍線的連接情況,一定是從某個節點的兒子 -> 某個節點 -> 它的父親。

明顯的換根 DP 。

設狀態f(u,0/1)f(u,0/1)爲當前在節點uu,其中00表示uu不是藍線的中點,而11表示uu是藍線的中點。

考慮f(u,0)f(u,0)的轉移,對於f(u,0)f(u,0),我們要使它不是中點,我們就要滿足對於它的任何一個兒子vv,它作爲中點(f(v,1)+wu,vf(v,1)+w_{u,v})和它也不是中點(f(v,0)f(v,0))。則我們得出f(u,0)f(u,0)的轉移:

f(u,0)=vson(u)max(f(v,1)+wu,v,f(v,0)) f(u,0)=\sum_{v\in son(u)}\max(f(v,1)+w_{u,v},f(v,0))

考慮f(u,1)f(u,1)的轉移,對於f(u,1)f(u,1),我們可以枚舉這條藍線連接的兒子vv,其他的點仍然按照f(u,0)f(u,0)的方法轉移,那麼對於vv,我們必須先減掉它原來的貢獻,再加上它現在的貢獻f(v,0)+wu,vf(v,0)+w_{u,v},所以得出f(u,1)f(u,1)的狀態轉移:

f(u,1)=f(u,0)+maxvson(u){f(v,0)+wu,vmax(f(v,0)+wu,v,f(v,1))} f(u,1)=f(u,0)+\max_{v\in son(u)}\left\{f(v,0)+w_{u,v}-\max(f(v,0)+w_{u,v},f(v,1))\right\}

這樣我們做出了以某個節點爲根的狀態轉移。

考慮換根:一個點的兒子在成爲它的父親之後,原來的兒子的貢獻就會消失,這個點的貢獻就會加到原來的兒子(現在的父親)上去。顯然必須記錄次大值

爲了方便地換根,我們另外定義狀態v(u,0/1,j)v(u,0/1,j)表示在計算uuff值時不考慮第jj棵子樹的代價,另外,爲了方便,我們也可以順帶記錄下去掉這個子樹後的最大值。

考慮換根時的一些細節。注意到一個點uu換了根後,它的父親會變成它的兒子(即對uu產生貢獻),所以我們應該先重新計算它的父親對它的貢獻,再來統計答案和換根。

參考代碼

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int Maxn = 200000;
const int INF = 0x3f3f3f3f;

int N;
vector<pair<int, int> > G[Maxn + 5];
void addedge(int u, int v, int w) {
	G[u].push_back(make_pair(v, w));
	G[v].push_back(make_pair(u, w));
}

int f[2][Maxn + 5];
vector<int> val[2][Maxn + 5], mx_son_val[Maxn + 5];
vector<int> son[Maxn + 5];
int len[Maxn + 5];
void PreDFS(int u, int fa) {
	f[0][u] = 0, f[1][u] = -INF;
	int mx1 = -INF, mx2 = -INF;
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i].first, w = G[u][i].second;
		if(v == fa) continue;
		PreDFS(v, u), len[v] = w;
		son[u].push_back(v);
		f[0][u] += max(f[0][v], f[1][v] + w);
		int tmp = f[0][v] + w - max(f[0][v], f[1][v] + w);
		if(tmp > mx1) mx2 = mx1, mx1 = tmp;
		else if(tmp > mx2) mx2 = tmp;
	}
	f[1][u] = f[0][u] + mx1;
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i].first, w = G[u][i].second;
		if(v == fa) continue;
		val[0][u].push_back(f[0][u] - max(f[0][v], f[1][v] + w));
		int tmp = f[0][v] + w - max(f[0][v], f[1][v] + w);
		if(tmp == mx1) {
			val[1][u].push_back(val[0][u].back() + mx2);
			mx_son_val[u].push_back(mx2);
		} else {
			val[1][u].push_back(val[0][u].back() + mx1);
			mx_son_val[u].push_back(mx1);
		}
	}
}
int ans;
void DFS(int u, int fa) {
	for(int i = 0; i < (int)son[u].size(); i++) {
		int v = son[u][i];
		f[0][u] = val[0][u][i], f[1][u] = val[1][u][i];
		if(fa != 0) {
			f[0][u] += max(f[0][fa], f[1][fa] + len[u]);
			f[1][u] = f[0][u] + max(mx_son_val[u][i], f[0][fa] + len[u] - max(f[0][fa], f[1][fa] + len[u]));
		}
		ans = max(ans, f[0][v] + max(f[0][u], f[1][u] + len[v]));
		DFS(v, u);
	}
}

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d", &N);
	for(int i = 1; i < N; i++) {
		int u, v, w;
		scanf("%d %d %d", &u, &v, &w);
		addedge(u, v, w);
	}
	PreDFS(1, 0);
	DFS(1, 0);
	printf("%d\n", ans);
	return 0;
}
//f[u][0/1]:
//f[u][0] = sum(max(f[v][0], f[v][1] + w(u, v)))
//f[u][1] = f[u][0] + max(f[v][0] + w(u, v) - max(f[v][0], f[v][1] + w(u, v)))
發佈了155 篇原創文章 · 獲贊 84 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章