建议到UOJ上去交

题解

一眼\(DP\),先把转移方程写出来

设\(dp[i]\)为从点\(i\)出发到点\(1\)的最小费用,那么存在转移

\[f[i]=min\{f[j]+(d[i]-d[j])p[i]\}+q[i]=min\{f[j]-d[j]p[i]\}+d[i]*p[i]+q[i]
\]

这个式子看起来可以斜率优化啊,往下化几步,可以得到类似下面的东西:

若\(d[j] < d[k]\),则当\(\frac{dp[j]-dp[k]}{d[j]-d[k]}\geqslant p[i]\)时从\(j\)转移更优,否则从\(k\)转移更优

这表明我们只需要维护一个下凸壳,转移时二分一下就行了

假设这个问题是在序列上且没有距离限制,你就已经可以\(O(nlogn)\)地\(A\)掉它了

加上距离限制时,我们可以拿个线段树维护一下每一小段的凸壳,查询时取个最大值

挪到树上时,只需要上个树剖

托上面两个东西的福,复杂度也变成了\(O(nlog^3n)\)[手动滑稽]

然后就是码码码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define MAXN 200000
  4. #define vi vector<int>
  5. #define pb push_back
  6. #define ll long long
  7. #define INF 0x7f7f7f7f7f7f7f7f
  8. #define LIM 17
  9. int n, t;
  10. vi G[MAXN + 5];
  11. int summit[MAXN + 5], f[20][MAXN + 5], fa[MAXN + 5], top[MAXN + 5], sz[MAXN + 5], hson[MAXN + 5], id[MAXN + 5], dfn[MAXN + 5], dfn_clk;
  12. ll d[MAXN + 5], s[MAXN + 5], p[MAXN + 5], q[MAXN + 5], l[MAXN + 5], dp[MAXN + 5];
  13. namespace HLD {
  14. void dfs1(int u) {
  15. sz[u] = 1;
  16. f[0][u] = fa[u];
  17. for (int i = 1; i <= LIM; ++i) f[i][u] = f[i - 1][f[i - 1][u]];
  18. for (auto v : G[u]) {
  19. if (v == fa[u]) continue;
  20. d[v] = d[u] + s[v];
  21. dfs1(v);
  22. sz[u] += sz[v];
  23. if (sz[v] > sz[hson[u]]) hson[u] = v;
  24. }
  25. }
  26. void dfs2(int u, int tp) {
  27. top[u] = tp;
  28. dfn[u] = ++dfn_clk;
  29. id[dfn_clk] = u;
  30. if (hson[u]) dfs2(hson[u], tp);
  31. for (auto v : G[u]) {
  32. if (v == fa[u] || v == hson[u]) continue;
  33. dfs2(v, v);
  34. }
  35. }
  36. }
  37. double slope(int x, int y) {
  38. return static_cast<double>(dp[y] - dp[x])/(d[y] - d[x]);
  39. }
  40. void pre() {
  41. for (int i = 2; i <= n; ++i) {
  42. int u = i;
  43. for (int j = LIM; ~j; --j)
  44. if (d[i] - d[f[j][u]] <= l[i])
  45. u = f[j][u];
  46. summit[i] = !u ? 1 : u;
  47. }
  48. }
  49. namespace SegementTree {
  50. #define mid ((l + r) >> 1)
  51. #define lson (o << 1)
  52. #define rson (o << 1 | 1)
  53. vi ch[4 * MAXN + 5];
  54. void addPoint(vi &v, int x) { // 把点x扔进下凸壳
  55. while (v.size() >= 2 && slope(x, v[v.size() - 2]) < slope(v[v.size() - 1], v[v.size() - 2])) v.pop_back();
  56. v.push_back(x);
  57. }
  58. ll get(vi &v, ll p) { // 在凸壳上二分斜率
  59. if (v.size() == 1) return dp[v[0]] - d[v[0]] * p;
  60. ll ret = INF;
  61. int l = 0, r = v.size() - 1, m;
  62. double s1, s2;
  63. while (l <= r) {
  64. m = (l + r) >> 1;
  65. if (m == v.size() - 1) {
  66. s1 = slope(v[m - 1], v[m]);
  67. ret = min(ret, dp[v[m]] - d[v[m]] * p);
  68. if (s1 <= p) l = m + 1;
  69. else r = m - 1;
  70. }
  71. else if (!m) {
  72. s2 = slope(v[m], v[m + 1]);
  73. ret = min(ret, dp[v[m]] - d[v[m]] * p);
  74. if (s2 <= p) l = m + 1;
  75. else r = m - 1;
  76. }
  77. else {
  78. s1 = slope(v[m - 1], v[m]);
  79. s2 = slope(v[m], v[m + 1]);
  80. ret = min(ret, dp[v[m]] - d[v[m]] * p);
  81. if (s1 <= p && s2 <= p) l = m + 1;
  82. else if(s1 <= p && s2 > p) return min(ret, dp[v[m]] - d[v[m]] * p);
  83. else r = m - 1;
  84. }
  85. }
  86. return ret;
  87. }
  88. void insert(int o, int l, int r, int x, int u) { // 插入一个点
  89. addPoint(ch[o], u);
  90. if (l == r) return ;
  91. if (x <= mid) insert(lson, l, mid, x, u);
  92. else insert(rson, mid + 1, r, x, u);
  93. }
  94. ll query(int o, int l, int r, int L, int R, ll p) { // 在那一堆凸壳中找最小值
  95. if (L <= l && r <= R) return get(ch[o], p);
  96. ll ret = INF;
  97. if (L <= mid) ret = min(ret, query(lson, l, mid, L, R, p));
  98. if (R > mid) ret = min(ret, query(rson, mid + 1, r, L, R, p));
  99. return ret;
  100. }
  101. #undef mid
  102. #undef lson
  103. #undef rson
  104. }
  105. using namespace SegementTree;
  106. void update(int u) { // 求点u的DP值
  107. dp[u] = INF;
  108. int x = fa[u];
  109. while (d[top[x]] >= d[summit[u]]) {
  110. dp[u] = min(dp[u], query(1, 1, n, dfn[top[x]], dfn[x], p[u]) + d[u] * p[u] + q[u]);
  111. x = fa[top[x]];
  112. }
  113. if (d[x] >= d[summit[u]]) dp[u] = min(dp[u], query(1, 1, n, dfn[summit[u]], dfn[x], p[u]) + d[u] * p[u] + q[u]);
  114. }
  115. void work(int u) {
  116. if (u != 1) update(u);
  117. insert(1, 1, n, dfn[u], u);
  118. for (auto v : G[u]) {
  119. if (v == fa[u]) continue;
  120. work(v);
  121. }
  122. }
  123. int main() {
  124. scanf("%d%d", &n, &t);
  125. for (int i = 2; i <= n; ++i) {
  126. scanf("%d%lld%lld%lld%lld", &fa[i], &s[i], &p[i], &q[i], &l[i]);
  127. G[fa[i]].pb(i);
  128. }
  129. HLD::dfs1(1);
  130. HLD::dfs2(1, 1);
  131. d[0] = -1;
  132. pre();
  133. work(1);
  134. for (int i = 2; i <= n; ++i) printf("%lld\n", dp[i]);
  135. return 0;
  136. }

[NOI2014]购票——斜率优化+树链剖分+线段树的更多相关文章

  1. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  2. 【bzoj2402】陶陶的难题II 分数规划+树链剖分+线段树+STL-vector+凸包+二分

    题目描述 输入 第一行包含一个正整数N,表示树中结点的个数.第二行包含N个正实数,第i个数表示xi (1<=xi<=10^5).第三行包含N个正实数,第i个数表示yi (1<=yi& ...

  3. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  4. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  5. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  6. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  7. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  8. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  9. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  10. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

随机推荐

  1. 【转帖】Linux上搭建Samba,实现windows与Linux文件数据同步

    Linux上搭建Samba,实现windows与Linux文件数据同步 2018年06月09日 :: m_nanle_xiaobudiu 阅读数 15812更多 分类专栏: Linux Samba 版 ...

  2. SpringBoot起步

    1.SpringBoot依赖包导入 方式一:将spring-boot的依赖为父pom出现 <parent> <groupId>org.springframework.boot& ...

  3. RabbitMQ快速开始

    ①:安装rabbitmq所需要的依赖包 yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make g ...

  4. 嵌入式linux第一阶段笔记

    1.虚拟网络编辑器(vm):三种模式:(VMnet0)桥接模式(vm和windows公用同个网络(同个物理端口)),(VMnet1)仅主机模式,(VMnet8)NAT模式(vm连接一个虚拟的路由(WA ...

  5. 实例详解jQuery的无new构建

    jQuery的无new构建 jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作. 回想一下使用 jQuery 的时候,实例化一个 jQuery 对象的方法: // 无 new 构造 $( ...

  6. MySQL 索引的优化

    一.MySQL如何使用索引(index) 1.1 索引概述 索引用于快速查找具有特定列值的行. 如果不使用索引,MySQL必须从表的第一行开始,然后扫描整个表来寻找符合条件的行.这种情况下,表越大,扫 ...

  7. Jersey 写restful接口时QueryParam ,FormParam 等的区别

    今天用jersey写接口,发现有一个post方法中没有得到参数,查了半天发现自己一不小心将@formparam写成了@queryparam,真是一个悲伤的故事.在这里把几个参数类型整理了一下放出来. ...

  8. SqlServer学习之存储过程

    前言:对于存储过程一直有一种抵触的心理,因为毕业至今所在的公司开发组都不是很规范,对于开发的一些注意事项并没有很多的规定,只是在知乎上查找相关知识的时候,看到很多人对于在程序里使用存储过程的不好之处都 ...

  9. JS基础_对象的方法

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. JavaScript-->基础类型和引用类型的区别

    先了解一下数组的基础知识:附代码(数组属于引用类型的对象) <!DOCTYPE html> <html lang="en"> <head> &l ...