Embiid  

B.Groundhog and Apple Tree

显然,在中途恢复体力等价于在根节点将所有需要的体力恢复完再走。那么我们考虑树DP。\(dp[u]\)表示遍历完\(u\)所在子树最少需要在\(u\)恢复的HP。\(res[u]\)表示遍历\(u\)这个子树的收益(正就是获得HP,负就是需要HP)。我们需要确定的是一种访问子树的顺序。我们假设确定了一种访问顺序,考虑计算在此种情况下需要的恢复时间\(need\)。我们转移\(dp[u]\)时,记录一个\(sum[v]=res[v] - 2*w_{u,v}\),表示从\(u\)点去访问子树\(v\)的收益。对于第\(i\)个访问的子树,我们需要起初至少有\(H[i]\)点HP
那么\(H[i]=cost[i]-\sum_{j=1}^{i-1} sum[j]\)。其中\(cost[i]\)表示从\(u\)遍历\(i\)子树需要的代价。
对于\(cost[i]\)的计算,有\(cost[i]=dp[i]+w_{u,i}+\delta\),访问子树\(i\)的时候,可能从\(i\)回到\(u\)HP不够,需要一个\(\delta\)的HP。我们如果在\(u\)恢复了\(dp[i]+w_{u,i}\)的体力后访问子树,到\(i\)准备返回\(u\)的时候剩余\(HP=dp[i]+w_{u,i}-w_{u,i}+res[i]=dp[i]+res[i]\),如果\(dp[i]+res[i]\geq w_{u,i}\)\(\delta=0\),否则\(\delta=w_{u,i}-dp[i]-res[i]\)
此时求出每个\(cost[i]\)\(H[i]\)。那么可以得到\(need=max_{i=1}^{son[u]} H[i]\)。对于所有的排列方式,我们有\(dp[u]=min(need)\)
如果某个访问顺序是最优的,那么交换任意\(i\)\(i+1\)都会不优。考虑交换\(i\)\(i+1\),改变的只有\(H[i],H[i+1]\),所以要有\(max(cost[i]-\sum_{j=1}^{i-1}sum[j],cost[i+1]-\sum_{j=1}^{i} sum[j])\leq max(cost[i+1]-\sum_{j=1}^{i-1} sum[j] , cost[i]-\sum_{j=1}^{i-1}sum[j]-sum[i+1])\).方便观察,我们全部消去一个\(A=\sum_{j=1}^{i-1}sum[j]\).原式化简为\(max(cost[i],cost[i+1]-sum[i])\leq max(cost[i+1],cost[i]-sum[i+1])\).我们进行分类讨论。

\(1.sum[i]<0,sum[i+1]<0\),那么会有\(cost[i+1]-sum[i]\leq cost[i]-sum[i+1]\)\(cost[i]+sum[i]\geq cost[i+1]+sum[i+1]\).

\(2.sum[i]\geq 0,sum[i+1]\geq 0\),那么会有\(cost[i]\leq cost[i+1]\)

\(3.sum[i]<0,sum[i+1]\geq 0\),恒不成立

\(4.sum[i]\geq 0,sum[i+1]<0\),恒成立

综上,我们先讲所有子节点分成两类分别排序就可以确定访问顺序了。

#include <bits/stdc++.h>
#define pb emplace_back
#define ll long long
#define lson rt << 1
#define rson rt << 1 | 1
#define all(x) (x).begin(),(x).end()
#define pii pair<int,int>
#define pll pair<ll,ll>
using namespace std;

const int maxn = 2e5;
ll sum[maxn + 11],dp[maxn + 11],res[maxn + 11];
int to[maxn + 11],w[maxn + 11],front[maxn + 11],nxt[maxn + 11],a[maxn + 11];
int cnt = 0;

void add(int u,int v,int _) { to[++cnt] = v; w[cnt] = _; nxt[cnt] = front[u]; front[u] = cnt; }

void dfs(int x,int fa) {
	sum[x] = a[x]; res[x] = a[x];
	for (int i = front[x]; i ; i = nxt[i]) {
		int v = to[i] , _ = w[i];
		if (v == fa) continue;
		dfs(v , x);
		sum[v] -= 2 * _; sum[x] += sum[v];
		res[x] -= 2 * _; res[x] += res[v];
	}
}

void solve(int x,int fa) {
	dp[x] = 0;
	vector <pll> A,B; ll cost = 0 , sal = 0;
	for (int i = front[x]; i ; i = nxt[i]) {
		int v = to[i] , _ = w[i];
		if (v == fa) continue;
		solve(v , x);
		cost = dp[v] + _; if (res[v] + dp[v] - _ < 0) cost -= res[v] + dp[v] - _;
		sal = sum[v];
		if (sal >= 0) A.pb(make_pair(cost , sal)); else B.pb(make_pair(cost , sal));
	}
	sort(all(A) , [&](pll x,pll y) { return x.first < y.first; } );
	sort(all(B) , [&](pll x,pll y) { return x.first + x.second > y.first + y.second; } );
	ll s = 0;
	for (auto pi : A) dp[x] = max(dp[x] , pi.first - s) , s += pi.second;
	for (auto pi : B) dp[x] = max(dp[x] , pi.first - s) , s += pi.second;
	dp[x] -= a[x]; dp[x] = max(dp[x] , 0ll);
} 

int main(){ 
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		for (int i = 1; i <= n; i++) cin >> a[i] , front[i] = 0;
		cnt = 0;
		for (int i = 1; i < n; i++) {
			int u , v , w; cin >> u >> v >> w;
			add(u , v , w); add(v , u , w);
		} 
		dfs(1 , 0);
		solve(1 , 0);
		cout << dp[1] << endl;
	}
} 
posted on 2020-08-25 19:27  Embiid  阅读(119)  评论(0编辑  收藏  举报