【HDU】【樹形DP】6662 Acesrc and Travel

HDU 6662 Acesrc and Travel

題目大意

◇題目傳送門◆

兩個人在一棵樹上博弈,AA走到點ii會獲得AiA_i的收益,BB走到節點ii會獲得BiB_i的收益。AA可以任意選擇一個起始點,然後BBAA選擇的點的相鄰點且沒有被選過的點中選擇一個走下去,然後AA進行相同的操作,現在兩人都想最大化與對手的差異,求AiBi\sum A_i-\sum B_i的最大值。

分析

爲了方便計算,我們記vi=AiBiv_i=A_i-B_i

由於兩人均想最大化與對手之間的差異,我們不難發現AA選擇了一個點後,BB一定會選擇相鄰點中viv_i最小的;反過來,AA一定會選擇最大的viv_i走下去。

那麼考慮這樣設計狀態:設狀態f1(u)f_1(u)爲當前兩人在節點uu,接下來該AA選點,走到以uu爲根的子樹中的某個葉子節點的最優的答案;同理,狀態f2(u)f_2(u)爲該BB選點時的最優答案。

不難列出狀態轉移:
f1(u)=maxvson(u){f2(v)+wu}f2(u)=minvson(u){f1(v)+wu} f_1(u)=\max_{v\in son(u)}\{f_2(v)+w_u\}\\f_2(u)=\min_{v\in son(u)}\{f_1(v)+w_u\}

這樣我們就做出了固定一個起點的轉移方案。

但這題的起點是不固定的,那麼我們考慮換根。

我們再定義g1(u),g2(u)g_1(u),g_2(u)分別爲現在在節點uu,輪到A/BA/B選點且從uu往父親走的方案。

那麼就有兩種轉移方式:(其中vvuu的兒子)

  • 往父親的父親方向:g1(v)=g2(u)+wv,g2(v)=g2(u)+wvg_1(v)=g_2(u)+w_v,g_2(v)=g_2(u)+w_v
  • 往兄弟方向:g1(v)=f1(u)+wv,g2(v)=f2(u)+wvg_1(v)=f_1(u)+w_v,g_2(v)=f_2(u)+w_v
  • 最後在兩種轉移方式中取較大值/較小值即可。

注意到我們gg的轉移方向可能和ff的最優值轉移方向相同,所以我們必須記錄一下次優值,當發現轉移方向是ff的最優值方向時就用次優值轉移。

最後答案爲maxi=1N{min(g1(i),f2(i))}\max_{i=1}^{N}\{\min(g_1(i),f_2(i))\}

注意當ii是葉子節點時,它對答案的貢獻僅爲g1(i)g_1(i)

參考代碼

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

typedef long long ll;
const int Maxn = 1e5;
const ll INF = 1e18;

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

int cnt_son[Maxn + 5];
ll f1[2][Maxn + 5], f2[2][Maxn + 5], g1[Maxn + 5], g2[Maxn + 5];
void PreDFS(int u, int fa) {
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i];
		if(v == fa) continue;
		PreDFS(v, u);
		cnt_son[u]++;
		if(f1[0][u] < f2[0][v] + val[u])
			f1[1][u] = f1[0][u], f1[0][u] = f2[0][v] + val[u];
		else if(f1[1][u] < f2[0][v] + val[u])
			f1[1][u] = f2[0][v] + val[u];
		if(f2[0][u] > f1[0][v] + val[u])
			f2[1][u] = f2[0][u], f2[0][u] = f1[0][v] + val[u];
		else if(f2[1][u] > f1[0][v] + val[u])
			f2[1][u] = f1[0][v] + val[u];
	}
	if(cnt_son[u] == 0) {
		f1[0][u] = f1[1][u] = val[u];
		f2[0][u] = f2[1][u] = val[u];
	}
}
void DFS(int u, int fa) {
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i];
		if(v == fa) continue;
		if(cnt_son[u] == 1) {
			g1[v] = g2[u] + val[v], g2[v] = g1[u] + val[v];
		} else {
			ll mx = f1[0][u];
			if(f1[0][u] == f2[0][v] + val[u])
				mx = f1[1][u];
			if(u != 1) mx = max(mx, g2[u]);
			g1[v] = mx + val[v];
			ll mn = f2[0][u];
			if(f2[0][u] == f2[0][v] + val[u])
				mn = f2[1][u];
			if(u != 1) mn = min(mn, g1[u]);
			g2[v] = mn + val[v];
		}
		DFS(v, u);
	}
}

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