ABC222F - Expensive Expense(换根DP)
ABC222F - Expensive Expense
解题思路
这个题是一道经典的换根 DP。这道题硬算是 \(O(n^2)\)。但是我们的时间限制要在 \(O(n)\) 以内。我们采用换根DP的技术。
首先我们先算出每个子树内的答案,再考虑每个子树外对答案的贡献。子树内的比较简单,主要思维难度在子树外部的答案计算上。
我们考虑现在计算根为 \(u\) 的子树。\(u\) 的父亲是 \(fa\),那么我们令状态为 \(g_u\),那么我们以 \(fa\) 为根时,维护出其每个子树的最大答案和次大的答案,然后扫一遍其原来树里的儿子。看起是不是最大答案,若是,那就用第二大的更新;否则用最大的更新。建议自己画图理解。我懒得画了
林外值得注意的是,每个节点不能以自己为终点,所以要注意一下。具体看代码。
最后注意一些细节,就可以了。
代码
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=2e5+10;
int n,cnt;
int h[MAXN],f[MAXN],g[MAXN],fir[MAXN],sec[MAXN],mx[MAXN],c[MAXN];
struct edge {
int to,w,nxt;
}e[MAXN<<1];
void addedge(int u,int v,int w) {
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=h[u];
h[u]=cnt;
}
void insert(int u,int v,int w) {
addedge(u,v,w);
addedge(v,u,w);
}
void dfs(int u,int fa) {
mx[u]=c[u];
for(int i=h[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v!=fa) {
dfs(v,u);
mx[u]=max(mx[u],mx[v]+e[i].w);
f[u]=max(f[u],mx[v]+e[i].w);
}
}
}
void efs(int u,int fa) {
int a=0,b=0;
for(int i=h[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v!=fa) {
if(mx[v]+e[i].w>fir[u]) {
sec[u]=fir[u];
b=a;
a=v;
fir[u]=mx[v]+e[i].w;
}
else if(mx[v]+e[i].w>sec[u]) {
b=v;
sec[u]=mx[v]+e[i].w;
}
}
}
for(int i=h[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v!=fa) {
g[v]=max((v==a? sec[u]:fir[u]),max(g[u],c[u]))+e[i].w;
efs(v,u);
}
}
}
signed main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n;
for(int i=1;i<n;i++) {
int u,v,w;
u=read(),v=read(),w=read();
insert(u,v,w);
}
for(int i=1;i<=n;i++) {
c[i]=read();
}
dfs(1,0);
efs(1,0);
for(int i=1;i<=n;i++) {
printf("%lld\n",max(g[i],f[i]));
}
//fclose(stdin);
//fclose(stdout);
return 0;
}