题面

Description

有一棵神奇的树。这棵树有N个节点,在每个节点上都有宝藏,每个宝藏价值V[i]金币;对于每条边,每经过一次都要花费C[i]金币。

值得注意的是,每个宝藏只能领取一次(也可以不领);但对于所有的边,如果要多次走这条边,将会多次产生费用。

我们定义 ans[i] 为从点 i 出发能获得的最大金币数量。

现在,请求出 ans[1], ans[2], ..., ans[N] 。

Input

第一行包含一个整数N (N≤105)(N≤105)

接下来一行将包含N个整数 V[i] ,表示每个宝藏价值的金币数 (1≤V[i]≤104)(1≤V[i]≤104) 。

接下来N-1行,每行包含三个整数u, v, c,表示u到v有一条边,每次走都要产生花费 c (1≤c≤104)(1≤c≤104) 。

Output

输出N行,第i行表示ans[i]。

Sample Input

5
4 1 7 7 7
1 2 6
1 3 1
2 4 8
3 5 2

Sample Output

15
10
14
9
15

HINT

对于5%的数据,N≤10N≤10

对于40%的数据,N≤1000N≤1000

对于100%的数据,N≤100000

Solution

虽然说这题的\(n \le 100000\), 但其正解仍然是线性做法.

树上DP. 我们要计算的是每个点往下走回来 / 不回来, 往上走回来 / 不回来的最大收益. 考虑到我们可能重复计算往上走后往下走的重复走某一段的情况, 我们还需要对往下走并且不回来的情况记录最大值和次大值.

总之很麻烦就对了.

总结经验:

  • 不要通过数据大小猜测正解的时间复杂度. 把一个方法想到底.
  • DP题假如实在不会的话, 果断跳过, 找思维量更小的数据结构题.
#include <cstdio>
#include <cctype>
#include <cstring>
#include <vector>
#include <algorithm> using namespace std;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1; char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)1e5;
struct tree
{
struct edge
{
int v, w;
inline edge(int _v, int _w) {v = _v; w = _w;}
};
struct node
{
vector<edge> edg;
int w, lst;
int flg;
int f; // 往下, 回
int mx[2], g[2]; // 往下, 不回
int upF, upG; // 往上, 回 / 不回
inline node()
{
edg.clear();
flg = 0;
f = 0;
mx[0] = mx[1] = -1; g[0] = g[1] = 0;
upF = upG = 0;
}
}nd[N + 1];
inline void addEdge(int u, int v, int w)
{
nd[u].edg.push_back(edge(v, w)); nd[v].edg.push_back(edge(u, w));
}
void down(const int u, const int pre, int w)
{
nd[u].lst = w;
nd[u].f = nd[u].w;
for(auto edg : nd[u].edg) if(edg.v != pre)
{
down(edg.v, u, edg.w);
if(nd[edg.v].f > edg.w * 2) nd[edg.v].flg = 1, nd[u].f += nd[edg.v].f - edg.w * 2;
}
nd[u].g[0] = nd[u].g[1] = nd[u].f;
for(auto edg : nd[u].edg) if(edg.v != pre)
{
int v = edg.v;
for(int i = 0; i < 2; ++ i)
if(nd[u].mx[i] == -1
&& nd[v].g[0] - nd[v].lst - nd[v].flg * (nd[v].f - nd[v].lst * 2) > 0
|| nd[u].mx[i] != -1
&& nd[v].g[0] - nd[v].lst - nd[v].flg * (nd[v].f - nd[v].lst * 2)
> nd[nd[u].mx[i]].g[0] - nd[nd[u].mx[i]].lst - nd[nd[u].mx[i]].flg * (nd[nd[u].mx[i]].f - nd[nd[u].mx[i]].lst * 2))
{
if(i == 0) nd[u].mx[1] = nd[u].mx[0];
nd[u].mx[i] = v;
break;
}
}
for(int i = 0; i < 2; ++ i) if(~ nd[u].mx[i])
nd[u].g[i] = nd[u].f
+ nd[nd[u].mx[i]].g[0] - nd[nd[u].mx[i]].lst
- nd[nd[u].mx[i]].flg * (nd[nd[u].mx[i]].f - nd[nd[u].mx[i]].lst * 2);
}
void up(const int u, const int pre)
{
if(~ pre)
{
nd[u].upF = nd[pre].upF + nd[pre].f - (nd[u].f - nd[u].lst * 2) * nd[u].flg - nd[u].lst * 2;
nd[u].upF = max(0, nd[u].upF);
int tmp;
if(u == nd[pre].mx[0]) tmp = nd[pre].g[1] - (nd[u].f - nd[u].lst * 2) * nd[u].flg;
else tmp = nd[pre].g[0] - (nd[u].f - nd[u].lst * 2) * nd[u].flg;
nd[u].upG = max(tmp + nd[pre].upF, nd[pre].f - (nd[u].f - nd[u].lst * 2) * nd[u].flg + nd[pre].upG) - nd[u].lst;
nd[u].upG = max(0, nd[u].upG);
}
for(auto edg : nd[u].edg) if(edg.v != pre) up(edg.v, u);
}
}T;
int main()
{ #ifndef ONLINE_JUDGE freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout); #endif using namespace Zeonfai;
int n = getInt();
for(int i = 1; i <= n; ++ i) T.nd[i].w = getInt();
for(int i = 1; i < n; ++ i)
{
int u = getInt(), v = getInt(), w = getInt();
T.addEdge(u, v, w);
}
T.down(1, -1, 0);
T.up(1, -1);
for(int i = 1; i <= n; ++ i)
// printf("%d %d ", T.nd[i].f + T.nd[i].upG, T.nd[i].upF + T.nd[i].g[0]);
printf("%d\n", max(T.nd[i].f + T.nd[i].upG, T.nd[i].upF + T.nd[i].g[0]));
// printf("%d %d %d %d\n", T.nd[i].f, T.nd[i].upG, T.nd[i].upF, T.nd[i].g[0]);
}

NOIP2016模拟赛三 Problem B: 神奇的树的更多相关文章

  1. NOIP2016模拟赛三 Problem C: 不虚就是要AK

    题目大意 给定一棵带有边权的树, 问你在树上随机选两个点, 它们最短路径上的边权之和为\(4\)的倍数的概率为多少. Solution 树分治. 没什么好讲的. #include <cstdio ...

  2. NOIp2018模拟赛三十六

    好久没打模拟赛了...今天一样是两道国集,一道bzoj题 成绩:13+0+95=108 A题开始看错题了...导致样例都没看懂,结果xfz提醒我后我理解了一个我自认为正确的题意(事实证明我和xfz都错 ...

  3. 串门赛: NOIP2016模拟赛——By Marvolo 丢脸记

    前几天liu_runda来机房颓废,顺便扔给我们一个网址,说这上面有模拟赛,让我们感兴趣的去打一打.一开始还是没打算去看一下的,但是听std说好多人都打,想了一下,还是打一打吧,打着玩,然后就丢脸了. ...

  4. [NOIP10.3模拟赛]3.w题解--神奇树形DP

    题目链接: 咕 闲扯: 这题考场上把子任务都敲满了,5个namespace,400行11k 结果爆0了哈哈,因为写了个假快读只能读入一位数,所以手测数据都过了,交上去全TLE了 把边分成三类:0. 需 ...

  5. NOIp2018模拟赛三十三

    神奇的一场... 成绩:100+0+14=114 A题是个体面很恐怖的题...然而看懂题意之后转化一下就变成了一道暴力傻逼题...但是不知道为什么dalao们都没写,讲题的时候挺尴尬的...yrx“瞄 ...

  6. NOIP2016 模拟赛

    7.10 T1:求出一个矩阵中平均数大于0的子矩阵的最大面积. T2:给出一个N行的,第I行有n+1-i的倒三角形,从中选取m个数,只有当前数的左上角和右上角都被选是才能选当前数,求选的数字的最大和 ...

  7. 学军NOIP2016模拟赛1

    GTMD这么水的一套题没有AK T1:妥妥的二分答案,贪心check. T2:问题可以转化为最长上升(还是下降我记不住了)子序列. T3:发现点被覆盖上的顺序是一定的.求出这个顺序,第一个操作在线段树 ...

  8. NOIp2018模拟赛三十八

    爆〇啦~ A题C题不会写,B题头铁写正解: 随手过拍很自信,出分一看挂成零. 若要问我为什么?gtmdsubtask! 神tm就一个subtask要么0分要么100,结果我预处理少了一点当场去世 难受 ...

  9. NOIp2018模拟赛三十七

    奇怪的一场... 前两题都是全场题,C题明显不可做,我题目都没看懂...(STO lhx OTZ) 成绩:100+100+8=208 貌似十几个208的...A题暴力$O(nmc)$能过...暴力容斥 ...

随机推荐

  1. IOS开发学习笔记018- 一般控件的使用

    1.移动 2.动画 3.缩放 3.旋转 4.简化代码 5.总结 UIButton 的两种状态 normal highlighted  1.移动 OC语法规定:不允许直接修改某个对象中结构体属性的成员. ...

  2. Leetcode 517.超级洗衣机

    超级洗衣机 假设有 n 台超级洗衣机放在同一排上.开始的时候,每台洗衣机内可能有一定量的衣服,也可能是空的. 在每一步操作中,你可以选择任意 m (1 ≤ m ≤ n) 台洗衣机,与此同时将每台洗衣机 ...

  3. 习题:烽火传递(DP+单调队列)

    烽火传递[题目描述]烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情.在某两座城市之间有n个烽火台,每个烽火台 ...

  4. BZOJ3295 [Cqoi2011]动态逆序对 【CDQ分治】

    题目 对于序列A,它的逆序对数定义为满足i 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为 ...

  5. 字符串函数 (strfun)

    字符串函数 (strfun) 题目描述 两个等长的由大写英文字母构成的字符串a和b,从a中选择连续子串x,从b中选出连续子串y.子串x与子串y的长度相等. 定义函数f(x,y)为满足条件xi=yi(1 ...

  6. linux之awk手册

    awk 手册   原文 Table of Contents 1. awk简介 2. awk命令格式和选项 2.1. awk的语法有两种形式 2.2. 命令选项 3. 模式和操作 3.1. 模式 3.2 ...

  7. codeforce div2 426 D. The Bakery

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

  8. EasySlider-最简洁的JQuery滚动插件 可控制滚动

    原文发布时间为:2010-05-05 -- 来源于本人的百度文章 [由搬家工具导入] Easy Silder是由Alen Grakalic开发的基于JQuery的滚动插件,它支持以下功能: 1.自动滚 ...

  9. 用正则表达式把页面中的px全部替换成rem

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  10. shell 执行提示No such file or directory

    问题描述: 项目开发过程中ansible执行脚本失败,单独运行shell脚本提示:No such file or directory,脚本结构执行脚本a 调用另一脚本b, 但查看b脚本路径正确不存在找 ...