宝藏

题目描述
一棵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
16
提示
对于20%的数据,满足n<=10。

对于50%的数据,满足n<=1000。

对于100%的数据,满足1<=n<=3*10^5, 1<=z, ai<=10^5。

显然是一道树形dp题目。考虑回到原点与不回到原点,g[u]表示回到u的最大价值,f[u]表示不回到u的最大价值。显然有g[u]=g[u]+max(0,g[v]-len[i]*2);
f[u]=max{g[u]-g[v]-len[i]+f[v]};
然后考虑根节点的转移,同样记录向上走回与不回的最大值次大值就可以了。详见代码,可能比较丑。

var
n,i,u,v,l,tot:longint;
head,ret,next,len,g,a:array[-2..800000] of longint;
f,p:array[-2..800000,0..2] of longint;
procedure ins(u,v,l:longint);
begin
  tot:=tot+1;
  ret[tot]:=v;
  len[tot]:=l;
  next[tot]:=head[u];
 head[u]:=tot;
end;

function max(a,b:longint):longint;
begin
  if a>b then exit(a) else exit(b);
end;

procedure dfs(u,pre:longint);
var
i,v,s,t,vv:longint;
begin
  i:=head[u];
  g[u]:=a[u];
  while i<>0 do
  begin
    v:=ret[i];
    if v<>pre then
    begin
      dfs(v,u);
      g[u]:=g[u]+max(0,g[v]-len[i]*2);
    end;
    i:=next[i];
  end;
  i:=head[u];
  f[u,1]:=a[u];
  f[u,2]:=a[u];
  while i<>0 do
  begin
    v:=ret[i];
    if (v<>pre) then
      if g[v]-len[i]*2>0 then
      begin
        s:=g[u]-g[v]+f[v,1]+len[i];
        vv:=v;
        if s>f[u,1] then
        begin
          t:=vv;
          vv:=p[u,1];
          p[u,1]:=t;
          t:=s;
          s:=f[u,1];
          f[u,1]:=t;
        end;
        if s>f[u,2] then
        begin
          p[u,2]:=vv;
          f[u,2]:=s;
        end;
      end
      else
      begin
         s:=g[u]+f[v,1]-len[i];
         vv:=v;
         if s>f[u,1] then
         begin
           t:=vv;
           vv:=p[u,1];
           p[u,1]:=t;
           t:=s;
           s:=f[u,1];
           f[u,1]:=t;
         end;
         if s>f[u,2] then
         begin
           p[u,2]:=vv;
           f[u,2]:=s;
         end;
      end;
    i:=next[i];
  end;
end;

procedure find(u,pre,l:longint);
var
gg,ff,i,v,s,ne,t,k,nex:longint;
begin
  gg:=g[pre];
  if g[u]-l*2>0 then gg:=gg-g[u]+l*2;
  if p[pre,1]<>u then
  begin
    ff:=f[pre,1];
    ne:=p[pre,1];
    if (g[u]-l*2>0) then ff:=ff-g[u]+l*2;
  end
  else
  begin
    ff:=f[pre,2];
    ne:=p[pre,2];
    if (g[u]-l*2>0)and(ne<>0) then ff:=ff-g[u]+l*2;
  end;
        s:=f[u,1]+gg-2*l;
        nex:=p[u,1];
        if s>f[u,1] then
        begin
          t:=nex;
          nex:=p[u,1];
          p[u,1]:=t;
          t:=s;
          s:=f[u,1];
          f[u,1]:=t;
        end;
        if s>f[u,2] then
        begin
          p[u,2]:=nex;
          f[u,2]:=s;
        end;
  g[u]:=g[u]+max(0,gg-l*2);
  if (gg-l*2>0) then
  begin
        s:=g[u]-gg+ff+l;
        if s>f[u,1] then
        begin
          t:=ne;
          ne:=p[u,1];
          p[u,1]:=t;
          t:=s;
          s:=f[u,1];
          f[u,1]:=t;
        end;
        if s>f[u,2] then
        begin
          p[u,2]:=ne;
          f[u,2]:=s;
        end;
  end
  else
  begin
         s:=g[u]+ff-l;
         if s>f[u,1] then
         begin
           t:=ne;
           ne:=p[u,1];
           p[u,1]:=t;
           t:=s;
           s:=f[u,1];
           f[u,1]:=t;
         end;
         if s>f[u,2] then
         begin
           p[u,2]:=ne;
           f[u,2]:=s;
         end;
  end;
  i:=head[u];
  while i<>0 do
  begin
    v:=ret[i];
    if v<>pre then
      find(v,u,len[i]);
    i:=next[i];
  end;
end;

begin
  readln(n);
  for i:=1 to n-1 do
  begin
    readln(u,v,l);
    ins(u,v,l);
    ins(v,u,l);
  end;
  for i:=1 to n do
    read(a[i]);
  dfs(1,-1);
  find(1,-1,0);
  for i:=1 to n do
    writeln(f[i,1]);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值