牛客小白月赛 22 B. 树上子链

链接

https://ac.nowcoder.com/acm/contest/4462/B

题意

带负权的点权树,求直径

思路

树形 DP

首先任选一点为根,记为 $root$ 并记 $dp[x]$ 为以 $x$ 为起点与其朝下所有节点的路径最大值,进行一次树形 DP:

$dp[i]=\max(\max \limits_{j \in son(i)}(dp[i],w[i]+dp[j]),w[i])$

我们可以发现最长子链其实是以某一点为起点的子链的最大值与次大值和(次大值和为正),且这两条路径没有重复边,因此我们在进行树形 DP从下到上转移时对 $res$ 进行更新:

$res=\max(\max \limits_{j \in son(i)}(res,dp[i]+dp[j]),dp[i])$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll w[N],dp[N];
int to[N*2],nxt[N*2],head[N],cnt;
bool st[N];
ll res=0xc0c0c0c0c0c0c0c0;
void addedge(int u,int v) {
cnt++;
to[cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u) {
st[u]=true;
bool f=false;
dp[u]=w[u];
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(st[v]) continue;
if(!f) f=true;
dfs(v);
res=max(res,dp[u]+dp[v]);
dp[u]=max(dp[u],dp[v]+w[u]);
}
res=max(res,dp[u]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<n;i++) {
int u,v;
cin>>u>>v;
addedge(u,v);
addedge(v,u);
}
memset(dp,0xc0,sizeof dp);
dfs(1);
cout<<res<<endl;
return 0;
}