【LOJ】【樹形DP】2485 「CEOI2017」Chase

LOJ 2485 「CEOI2017」Chase

題目大意

◇題目傳送門◆

似乎壓縮起來有點困難,所以就不壓縮了吧 QwQ…

分析

考慮扔一個磁鐵能夠產生的讓逃亡者和追逐者之間的差異。

這個差異就是這個節點的所有兒子的點權之和(因爲父親和它的貢獻已經被計算過一次了)。

不難設狀態f(u,i)f(u,i)爲在以uu爲根的子樹上的某個節點出發,扔ii個磁鐵,最終停留在uu能夠產生的最大貢獻。另設vuv_u爲與uu相鄰的點的點權和,aua_uuu的點權。

不難列出狀態轉移方程:

f(u,i)=maxvson(u){f(v,i),f(v,i1)+vuav} f(u,i)=\max_{v\in son(u)}\{f(v,i),f(v,i-1)+v_u-a_v\}

f(u,i)f(u,i)的初值爲vu(i0),0(i=0)v_u(i\neq 0),0(i=0)

由於根不固定,所以我們考慮換根。

g(u,i)g(u,i)爲從節點uu向子樹內走,扔下ii個磁鐵所能夠產生的最大貢獻。

轉移顯然可以寫成(其中wwuu的父親)

g(u,i)=maxvson(u){g(v,i),g(v,i1)+vuaw} g(u,i)=\max_{v\in son(u)}\{g(v,i),g(v,i-1)+v_u-a_w\}

但是我們必須去重(有可能f,gf,g的路徑都選到了同一個兒子),所以我們在更新f,gf,g開始前就更新答案,即先做:
ans=maxi=1V{f(u,i)+g(u,Vi)} ans=\max_{i=1}^{V}\{f(u,i)+g(u,V-i)\}

再對f,gf,g進行轉移。

考慮對於uu子樹中的兩個節點v,wv,w,一種情況是從vv走上uu再走下ww,另一種情況是從ww走上uu再走下vv,這兩種情況得到的答案是很有可能不一樣的。

而在我們剛纔的轉移中,由於爲了防止路徑重複先更新了答案,這表明我們所得到的ff選取的路徑一定是在gg選取的路徑的左邊。也就是說我們必須統計ggff左邊的情況。

一種比較有效的方法是將vv壓進一個棧裏,再從棧中取出,一個個更新即可。

注意答案還需和max(f(u,V),g(u,V))\max(f(u,V),g(u,V))進行比較

參考代碼

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

typedef long long ll;
const int Maxn = 1e5;
const int Maxv = 100;

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

ll val[Maxn + 5]; 
ll f1[Maxn + 5][Maxv + 5], f2[Maxn + 5][Maxv + 5];
ll ans;
void calc(int x, int y, int z) {
	for(int i = 1; i <= V; i++)
		ans = max(ans, f1[x][i] + f2[y][V - i]);
	for(int i = 1; i <= V; i++) {
		f1[x][i] = max(f1[x][i], max(f1[y][i], f1[y][i - 1] + val[x] - A[y]));
		f2[x][i] = max(f2[x][i], max(f2[y][i], f2[y][i - 1] + val[x] - A[z]));
	}
}
void DFS(int u, int fa) {
	for(int i = 1; i <= V; i++)
		f1[u][i] = val[u], f2[u][i] = val[u] - A[fa];
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i];
		if(v == fa) continue;
		DFS(v, u);
		calc(u, v, fa);
	}
	stack<int> stk;
	for(int i = 1; i <= V; i++)
		f1[u][i] = val[u], f2[u][i] = val[u] - A[fa];
	for(int i = 0; i < (int)G[u].size(); i++){
		int v = G[u][i];
		if(v == fa) continue;
		stk.push(v);
	}
	while(!stk.empty()) {
		int v = stk.top();
		stk.pop(), calc(u, v, fa);
	}
	ans = max(ans, max(f1[u][V], f2[u][V]));
}

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