NOIP2016模拟赛三 Problem B: 神奇的树
题面
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: 神奇的树的更多相关文章
- NOIP2016模拟赛三 Problem C: 不虚就是要AK
题目大意 给定一棵带有边权的树, 问你在树上随机选两个点, 它们最短路径上的边权之和为\(4\)的倍数的概率为多少. Solution 树分治. 没什么好讲的. #include <cstdio ...
- NOIp2018模拟赛三十六
好久没打模拟赛了...今天一样是两道国集,一道bzoj题 成绩:13+0+95=108 A题开始看错题了...导致样例都没看懂,结果xfz提醒我后我理解了一个我自认为正确的题意(事实证明我和xfz都错 ...
- 串门赛: NOIP2016模拟赛——By Marvolo 丢脸记
前几天liu_runda来机房颓废,顺便扔给我们一个网址,说这上面有模拟赛,让我们感兴趣的去打一打.一开始还是没打算去看一下的,但是听std说好多人都打,想了一下,还是打一打吧,打着玩,然后就丢脸了. ...
- [NOIP10.3模拟赛]3.w题解--神奇树形DP
题目链接: 咕 闲扯: 这题考场上把子任务都敲满了,5个namespace,400行11k 结果爆0了哈哈,因为写了个假快读只能读入一位数,所以手测数据都过了,交上去全TLE了 把边分成三类:0. 需 ...
- NOIp2018模拟赛三十三
神奇的一场... 成绩:100+0+14=114 A题是个体面很恐怖的题...然而看懂题意之后转化一下就变成了一道暴力傻逼题...但是不知道为什么dalao们都没写,讲题的时候挺尴尬的...yrx“瞄 ...
- NOIP2016 模拟赛
7.10 T1:求出一个矩阵中平均数大于0的子矩阵的最大面积. T2:给出一个N行的,第I行有n+1-i的倒三角形,从中选取m个数,只有当前数的左上角和右上角都被选是才能选当前数,求选的数字的最大和 ...
- 学军NOIP2016模拟赛1
GTMD这么水的一套题没有AK T1:妥妥的二分答案,贪心check. T2:问题可以转化为最长上升(还是下降我记不住了)子序列. T3:发现点被覆盖上的顺序是一定的.求出这个顺序,第一个操作在线段树 ...
- NOIp2018模拟赛三十八
爆〇啦~ A题C题不会写,B题头铁写正解: 随手过拍很自信,出分一看挂成零. 若要问我为什么?gtmdsubtask! 神tm就一个subtask要么0分要么100,结果我预处理少了一点当场去世 难受 ...
- NOIp2018模拟赛三十七
奇怪的一场... 前两题都是全场题,C题明显不可做,我题目都没看懂...(STO lhx OTZ) 成绩:100+100+8=208 貌似十几个208的...A题暴力$O(nmc)$能过...暴力容斥 ...
随机推荐
- Spring---基于Spring IOC的小程序
实现的功能以及各文件间的关系 IHelloMessage:一个接口,用于定义输出问候信息. HelloWorld.HelloChina:接口的实现类.在这里表示人在不同的地方 Person:一个人物类 ...
- 云容器和安全性仍然是困扰IT人士的头号问题
[TechTarget中国原创] 容器和云安全仍然是IT领域中最热门的两个话题.下面就让我们来详细探讨一下吧. 云容器风靡一时是事出有因的.如Docker这样的容器能够提高应用的可移植性,并让企业用户 ...
- 使用WMI Filter 实现组策略的筛选!
今天接到一个客户的一个问题,提到需要分系统版本分发相应的MSI程序.比如简体版接受简体版的分发程序,繁体版接受繁体版的分发程序!这个建立组策略的不同版本分发本身不会太难,我们只需要建立两个不同组策略分 ...
- Python+Selenium中级篇之-二次封装Selenium中几个方法
本文来介绍,如何把常用的几个webdriver的方法封装到自己写的一个类中去,这个封装过程叫二次封装Selenium方法.我们把打开站点,浏览器前进和后退,关闭和退出浏览器这这个方法封装到一个新写的类 ...
- web自动化测试:watir+minitest(一)
基本介绍: 本课程的测试环境和工具为:win7+ruby+watir+minitest Watir 全称是"Web Application Testing in Ruby".它是一 ...
- sql执行效率 Explain
explain+sql语句 explain返回的结果项很多,这里我们只关注三种,分别是type,key,rows. key:显示MySQL实际决定使用的键(索引).如果没有选择索引,键是NULL. r ...
- Python Spider
一.网络爬虫 网络爬虫又被称为网络蜘蛛(
- C++ vector 的 begin()、end()、front()、back() 区别
STL中实现源码可见:http://www.cplusplus.com/reference/vector/vector/begin/ 一.begin函数 函数原型: iterator begin(); ...
- Python之数据结构:字符串
一.字符串类型 1.普通字符串 s1='abef\neiwo' print s1 print type(s1) 结果: abef eiwo <type 'str'> 2.原始字符串 s2= ...
- 大陆争霸(bzoj 1922)
Description 在一个遥远的世界里有两个国家:位于大陆西端的杰森国和位于大陆东端的 克里斯国.两个国家的人民分别信仰两个对立的神:杰森国信仰象征黑暗和毁灭 的神曾·布拉泽,而克里斯国信仰象征光 ...