题目描述
一棵n个点的树,到达一个点会获得这个点上的宝藏,每个宝藏都有一定的价值。经过每条边需要支付一定的过路费。每个点只有一个宝藏,但过路费每次都要交。求从每个点出发能得到的最大收益。
输入
输入文件为treasure.in。
第一行为一个正整数n。
接下来n-1行,每行三个整数x, y, z,描述一条边的两个端点x, y和过路费z。
最后一行n个数,表示每个点上宝藏的价值ai。
输出
输出文件为treasure.out。
输出n行,每行一个数。第i行表示从i出发的最大收益。
样例输入
6
1 2 1
2 3 3
3 4 36
3 6 13
3 5 2
6 8 9 10 13 1
样例输出
30
29
28
10
30
16
提示
对于20%的数据,满足n<=10。
对于50%的数据,满足n<=1000。
对于100%的数据,满足1<=n<=
3∗105
, 1<=z, ai<=
105
。
Solution
travel的升级版,以1为根往下做一遍,往上做一遍。
写的过程真是悲壮感人。。。(话说为什么联考里的dp全是树形的)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,x,y,z,tot;
int head[300005],Next[600005],to[600005],len[600005];
int a[300005];
ll f[300005],g[300005];//f回到i,g不回到i
ll sf[300005],sg[300005];
ll son_down[300005],son_up[300005];
void add(int x,int y,int z)
{
tot++;
Next[tot]=head[x];
to[tot]=y;
len[tot]=z;
head[x]=tot;
}
void dp(int k,int pre)
{
f[k]=a[k];
g[k]=a[k];
ll s=0;
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
dp(to[i],k);
s=s+max(0LL,f[to[i]]-2*len[i]);//去当前子树or不去
}
son_down[k]=s; //从底向下做的时候,记录k儿子的信息
f[k]=s+a[k]; //势必要回到i
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
ll left=s-max(0LL,f[to[i]]-2*len[i]); //其他子树还要去
g[k]=max(g[k],left+max(g[to[i]],f[to[i]])-len[i]+a[k]);
}
}
void DP(int k,int pre)
{
ll s=0,left=0;
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre) s=s+max(0LL,f[to[i]]-2*len[i]);
s=s+max(0LL,sf[k]);
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
left=s-max(0LL,f[to[i]]-2*len[i]);
son_up[to[i]]=max(son_up[to[i]],left-2*len[i]);
sf[to[i]]=max(sf[to[i]],left-2*len[i]+a[to[i]]);
}
ll s1=0,s2=0;
int p1=0,p2=0;
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
left=s-max(0LL,f[to[i]]-2*len[i])+g[to[i]]-len[i];
if(left>s1)
{
s2=s1;
p2=p1;
s1=left;
p1=to[i];
}
else
if(left>s2)
{
s2=left;
p2=to[i];
}
}
left=s-max(0LL,sf[k])+sg[k];
if(left>s1)
{
s2=s1;
p2=p1;
s1=left;
p1=k;
}
else
if(left>s2)
{
s2=left;
p2=k;
}
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre)
{
if(p1==to[i])
{
sg[to[i]]=max(sg[to[i]],s2-len[i]+a[to[i]]-max(0LL,f[to[i]]-2*len[i]));
}
else
{
sg[to[i]]=max(sg[to[i]],s1-len[i]+a[to[i]]-max(0LL,f[to[i]]-2*len[i]));
}
}
for(int i=head[k];i!=-1;i=Next[i])
if(to[i]!=pre) DP(to[i],k);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sf[i]=sg[i]=a[i];
}
dp(1,0);
DP(1,0);
for(int i=1;i<=n;i++) printf("%lld\n",max(max(sf[i]+f[i]-a[i],g[i]+son_up[i]),sg[i]+son_down[i]));
return 0;
}