hdu5834 Magic boy Bi Luo with his excited tree
-
题意:一棵树,每个点有点权,边有边权,从一点出发,遍历树,每次遍历到一条边就减去边权,第一次遍历到一个点就加上点权(后续不加)。问从所有点出发能得到最大价值。
-
刚碰这题认为这题和洛谷的Kamp很像,都是点权一次(点权可以设置为0),边权多次。但是洛谷的kamp是给定了遍历到哪些点,而这题是所有点都可以被遍历到。这也就导致了,Kamp解题思路中的边权值*2加上最长边的“最长边”这一个信息很难在这题中被维护。
-
所以,我们需要换一个思路。
-
提示: u u u表示当前结点, v v v表示 u u u的儿子结点。
-
我们设 d p [ u ] [ 0 / 1 / 2 ] dp[u][0/1/2] dp[u][0/1/2]分别代表**回到点 u u u**的最大价值,不回到点 u u u的最大价值,不回到点 u u u的次大价值。其中不回答 u u u的最大价值次大价值其实就是我们加上的最长边所对应的 v v v不一样(为何要维护次大价值后续再说)
-
小tip: d p [ u ] [ 1 ] − d p [ u ] [ 0 ] = m a x l e n dp[u][1] - dp[u][0]=maxlen dp[u][1]−dp[u][0]=maxlen,其中 m a x l e n maxlen maxlen就是上面说的根节点到遍历的所有点中的最长边。换句话说其实 d p [ u ] [ 1 ] dp[u][1] dp[u][1]也可以由 m a x l e n + d p [ u ] [ 0 ] maxlen+dp[u][0] maxlen+dp[u][0]得到。再换句话说,对于 d p [ u ] [ 1 ] dp[u][1] dp[u][1]其实就是遍历的所有点中的多条边中,除了最长边减了1次,其他都减了两次。
- 所以 d p [ u ] [ 0 ] dp[u][0] dp[u][0]的很好递推,就是 d p [ u ] [ 0 ] + = m a x ( 0 , d p [ v ] [ 0 ] − 2 ∗ e d g e [ i ] . w ) dp[u][0]+=max(0, dp[v][0] - 2 * edge[i].w) dp[u][0]+=max(0,dp[v][0]−2∗edge[i].w)。这里取 m a x max max是因为对于这个点可取可不取。
- d p [ u ] [ 1 ] dp[u][1] dp[u][1]就需要用到 d p [ u ] [ 0 ] dp[u][0] dp[u][0]了。同理 d p [ u ] [ 2 ] dp[u][2] dp[u][2]这个次大值也需要实时更新。
- d p [ u ] [ 1 ] dp[u][1] dp[u][1]需要讨论,对于一个儿子 v v v如果 2 ∗ e d g e [ i ] . w − d p [ v ] [ 0 ] < = 0 2 * edge[i].w-dp[v][0]<=0 2∗edge[i].w−dp[v][0]<=0表示 d p [ u ] [ 0 ] dp[u][0] dp[u][0]没有取过这个点。但是对于 d p [ u ] [ 1 ] dp[u][1] dp[u][1]他是可能取到这个点的。我们看小tip中的加粗字体,可以知道虽然我减 v v v这个儿子所对应的边两次不合适,但是可以只减一次。即这个点所对应的边可能是上述所说的最长边。 t m p = d p [ u ] [ 0 ] − e d g e [ i ] . w + d p [ v ] [ 1 ] ; tmp = dp[u][0] - edge[i].w + dp[v][1]; tmp=dp[u][0]−edge[i].w+dp[v][1];就是表示只取了一次。
- 如果 2 ∗ e d g e [ i ] . w − d p [ v ] [ 0 ] > 0 2 * edge[i].w-dp[v][0]>0 2∗edge[i].w−dp[v][0]>0表示 d p [ u ] [ 0 ] dp[u][0] dp[u][0]取过这个边。同理,取过的话我们也要看看这个边只减去一次是不是最优的 d p [ u ] [ 1 ] dp[u][1] dp[u][1]即这个点对应的边可能也是上述说的最长边。 t m p = d p [ u ] [ 0 ] + e d g e [ i ] . w + d p [ v ] [ 1 ] − d p [ v ] [ 0 ] ; tmp = dp[u][0] + edge[i].w + dp[v][1] - dp[v][0]; tmp=dp[u][0]+edge[i].w+dp[v][1]−dp[v][0];这个方程整体含义是因为 d p [ u ] [ 0 ] dp[u][0] dp[u][0]取过这个点,所以我们要先减去这个点对 d p [ u ] [ 0 ] dp[u][0] dp[u][0]的影响,然后加上只取一次的影响。
-
dfs2
- 我们要理清思路。一个点不回到 u u u的最大价值无非就是最长边在子树的,或者最长边经过父节点的。而最长边在子树的,我们dfs1已经得到了,所以dfs2是用来得到最长边在父节点的价值。
- 但是最长边在父节点我们就遇到了问题,我们发现我们通过父节点更新这个 v v v并不简单。
- 假设父节点的最长边经过的点 m a x s o n [ u ] [ 1 ] maxson[u][1] maxson[u][1]不是我们现在遍历的 v v v。我们可以通过父节点的 d p [ u ] [ 1 ] dp[u][1] dp[u][1]再减去 v v v对 d p [ u ] [ 1 ] dp[u][1] dp[u][1]的贡献,然后统计三个最大值,一个是最长边经过父节点 u u u的,一个是最长边在儿子的并且通过父节点取到其他点的,一个是最长边在儿子,但是不同过父节点取到其他点的。之后就是维护细节。
- 但是如果 m a x s o n [ u ] [ 1 ] = = v maxson[u][1]==v maxson[u][1]==v的话,我们发现我们要统计的两个最大值中的最长边经过父节点 u u u的很难得到。所以我们需要为何次优值。来实现这个。
-
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10, M = 2e5 + 10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int>pii;
typedef long long ll;
int head[MAXN], cnt = 0, val[MAXN];
int maxson[MAXN][3];
int dp[MAXN][3];
struct Edge{
int to, next, w;
}edge[MAXN * 2];
inline void addedge(int u, int v, int w) {
edge[cnt] = {v, head[u], w};head[u] = cnt++;
edge[cnt] = {u, head[v], w};head[v] = cnt++;
}
void dfs1(int u, int fa){
dp[u][0] = dp[u][1] = dp[u][2] = val[u];
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(v == fa)continue;
dfs1(v, u);
dp[u][1] = dp[u][2] = dp[u][0] = dp[u][0] + max(0, dp[v][0] - 2 * edge[i].w);
}
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(v == fa)continue;
int tmp;
if(dp[v][0] - 2 * edge[i].w <= 0)tmp = dp[u][0] - edge[i].w + dp[v][1];
else tmp = dp[u][0] + edge[i].w + dp[v][1] - dp[v][0];
if(tmp > dp[u][1]){
dp[u][2] = dp[u][1];
maxson[u][2] = maxson[u][1];
dp[u][1] = tmp;
maxson[u][1] = v;
}else if(tmp > dp[u][2]){
dp[u][2] = tmp;
maxson[u][2] = v;
}
}
}
void dfs2(int u, int fa){
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(v == fa)continue;
int tmp;
if(v == maxson[u][1])tmp = dp[u][2];
else tmp = dp[u][1];
if(dp[v][0] - 2 * edge[i].w > 0){
if(tmp + edge[i].w >= max(0, dp[u][0] - dp[v][0]) + dp[v][1]){
dp[v][2] = max(0, dp[u][0] - dp[v][0]) + dp[v][1];maxson[v][2] = maxson[v][1];
dp[v][1] = tmp + edge[i].w; maxson[v][1] = u;
}else{
dp[v][1] = max(0, dp[u][0] - dp[v][0]) + dp[v][1];
dp[v][2] = max(0, dp[u][0] - dp[v][0]) + dp[v][2];
if(tmp + edge[i].w > dp[v][2]){
dp[v][2] = tmp + edge[i].w; maxson[v][2] = u;
}
}
dp[v][0] = max(0, dp[u][0] - dp[v][0]) + dp[v][0];
}else{
if(tmp - edge[i].w + dp[v][0] >= max(dp[u][0] - 2 * edge[i].w, 0) + dp[v][1]){
dp[v][2] = max(dp[u][0] - 2 * edge[i].w, 0) + dp[v][1];maxson[v][2] = maxson[v][1];
dp[v][1] = tmp - edge[i].w + dp[v][0]; maxson[v][1] = u;
}else{
dp[v][1] = max(dp[u][0] - 2 * edge[i].w, 0) + dp[v][1];
dp[v][2] = max(dp[u][0] - 2 * edge[i].w, 0) + dp[v][2];
if(tmp - edge[i].w + dp[v][0] > dp[v][2]){
dp[v][2] = tmp - edge[i].w + dp[v][0]; maxson[v][2] = u;
}
}
dp[v][0] = max(0, dp[u][0] - 2 * edge[i].w) + dp[v][0];
}
dfs2(v, u);
}
}
int main() {
int t;
scanf("%d", &t);
int tt = 0;
while(t--){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
head[i] = -1;
maxson[i][0] = maxson[i][1] = 0;
dp[i][0] = dp[i][1] =dp[i][2] = 0;
}
cnt = 0;
for(int i = 1; i <= n; i++)cin >> val[i];
for(int i = 1; i <= n - 1; i++){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
dfs1(1, 0);
dfs2(1, 0);
printf("Case #%d:\n", ++tt);
for(int i = 1; i <= n; i++){
printf("%d\n", dp[i][1]);
}
}
return 0;
}