题目链接

UOJ #7

题解

首先这一定是DP!可以写出:
\[f[i] = \min_{ancestor\ j} \{f[j] + (d[j] - d[i]) * p[i] + q[i]\}\]
其中\(d[i]\)表示树上\(i\)的深度。
整理一下式子:
\[f[i] = \min_{ancestor\ j} \{f[j] - d[j] * p[i]\} + d[i] * p[i] + q[i]\]
看起来可以斜率优化?
推一下式子:设\(j < k\),\(i\)从\(j\)转移优于从\(k\)转移:
\[f[j] - d[j] * p[i] < f[k] - d[k] * p[i]\]
\[\frac{f[k] - f[j]}{d[k] - d[j]} > p[i]\]
好的!
所以应该维护一个下凸壳,在上面二分即可。

可是由于限制条件,每个结点\(i\)对应的下凸壳都是不同的,怎么办呢?

考虑一条链的情况:每个\(f[i]\)都是可以由一个区间内的凸包得到。

可以用线段树维护当前处理完的所有点的凸包,线段树上每个节点上存储着一个凸包,查询的时候相当于在线段树上区间查询——如果当前节点所代表的区间完全包含在查询区间里面,则在这个凸包上二分查询这个区间可以带来的最优解,否则递归,就可以得到答案了。

现在再考虑把一条链上的情况推广到树上。

考虑DFS,栈中的节点组成从根到当前节点的一条链,如果线段树维护了这条链的信息,则可以像正常序列上的情况一样求当前点的\(f\)值。
如果当前点DFS完毕出栈时,可以在线段树上删除它,就可以不影响复杂度地保证时时刻刻线段树维护的都是栈中所有节点的信息,就可以求出答案了。

于是引入【可撤销的凸包】,每次可以【撤销】——回到上一次插入新节点的操作之前。
怎么实现?当插入一个新节点时,二分它要放到哪个位置,并记录当前的top和被新节点取代的节点是谁,当撤销这次插入时,恢复top和被取代的节点即可。这样插入是\(O(\log n)\)的,撤销操作是\(O(1)\)的,非常科学 =v=

代码比想象中好写(雾

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){char c;bool op = 0;while(c = getchar(), c < '0' || c > '9')if(c == '-') op = 1;x = c - '0';while(c = getchar(), c >= '0' && c <= '9')x = x * 10 + c - '0';if(op) x = -x;
}
template <class T>
void write(T x){if(x < 0) putchar('-'), x = -x;if(x >= 10) write(x / 10);putchar('0' + x % 10);
}const int N = 200005;
int n, T, adj[N], nxt[N], fa[N], d_top, id[N];
ll f[N], d[N], p[N], q[N], w[N], lim[N], d_stk[N];
int seg_dep[4*N], pre[N][20], last_top[N][20], top[4*N];
vector <int> stk[4*N];
typedef long double ld;
//pre[i][j]是i号节点在线段树上第j层加入所属的凸包中时, "取代"的点的编号, last[i][j]是加入前该凸包的大小
void build(int k, int l, int r){seg_dep[k] = seg_dep[k >> 1] + 1;stk[k].resize(r - l + 3);if(l == r) return;int mid = (l + r) >> 1;build(k << 1, l, mid);build(k << 1 | 1, mid + 1, r);
}
bool cmp1(int i, int j, int k){ // 判断(i, j, k)是否构成上凸return (ld)(d[j] - d[i]) * (f[k] - f[j]) < (ld)(f[j] - f[i]) * (d[k] - d[j]);
}
int find_pos(int k, int i){ // 在k号单调栈中插入i号点, 应该放在哪个位置if(!top[k]) return 1;int l = 2, r = top[k] + 1, mid; // 找到第一个和i上凸的位置(新来的i应该取代这个位置)while(l < r){mid = (l + r) >> 1;if(cmp1(stk[k][mid - 1], stk[k][mid], i)) r = mid;else l = mid + 1;}return l;
}
void push(int k, int i){int p = find_pos(k, i);last_top[i][seg_dep[k]] = top[k];pre[i][seg_dep[k]] = stk[k][p];top[k] = p;stk[k][p] = i;
}
void rollback(int k){int i = stk[k][top[k]];stk[k][top[k]] = pre[i][seg_dep[k]];top[k] = last_top[i][seg_dep[k]];
}
ll calc(int u, int v){return f[u] + (d[v] - d[u]) * p[v] + q[v];
}
bool cmp2(int i, int j, ll x){ // 判断i和j构成的斜率是否小于等于xreturn f[j] - f[i] <= (ld) x * (d[j] - d[i]);
}
ll ask(int k, int x){int l = 1, r = top[k];while(l < r){int mid = (l + r) >> 1;if(cmp2(stk[k][mid], stk[k][mid + 1], p[x])) l = mid + 1;else r = mid;}return calc(stk[k][l], x);
}
void insert(int k, int l, int r, int p, bool flag){flag ? push(k, id[p]) : rollback(k);if(l == r) return;int mid = (l + r) >> 1;if(p <= mid) insert(k << 1, l, mid, p, flag);else insert(k << 1 | 1, mid + 1, r, p, flag);
}
ll query(int k, int l, int r, int ql, int qr){if(ql <= l && qr >= r) return ask(k, id[qr + 1]);int mid = (l + r) >> 1;ll ret = 9e18;if(ql <= mid) ret = query(k << 1, l, mid, ql, qr);if(qr > mid) ret = min(ret, query(k << 1 | 1, mid + 1, r, ql, qr));return ret;
}
void dfs(int u){d_stk[++d_top] = d[u] = d[fa[u]] + w[u], id[d_top] = u;if(u != 1){int st = lower_bound(d_stk + 1, d_stk + d_top + 1, d[u] - lim[u]) - d_stk;f[u] = query(1, 1, n, st, d_top - 1);}insert(1, 1, n, d_top, 1);for(int v = adj[u]; v; v = nxt[v]) dfs(v);insert(1, 1, n, d_top, 0);d_top--;
}int main(){read(n), read(T);build(1, 1, n);for(int i = 2; i <= n; i++){read(fa[i]), read(w[i]), read(p[i]), read(q[i]), read(lim[i]);nxt[i] = adj[fa[i]], adj[fa[i]] = i;}dfs(1);for(int i = 2; i <= n; i++)write(f[i]), enter;return 0;
}

转载于:https://www.cnblogs.com/RabbitHu/p/UOJ7.html

UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP相关推荐

  1. 【BZOJ5461】 【PKUWC2018】—Minimax(线段树合并优化dp)

    传送门 发现其实就是左右2棵子树,左儿子选到某个值的概率就是 选最大值的概率∗右儿子的值比它小的概率选最大值的概率*右儿子的值比它小的概率选最大值的概率∗右儿子的值比它小的概率 +选最小值的概率∗右儿 ...

  2. 【uoj】198:【CTSC2016】时空旅行-dfs序线段树凸包

    传送门:uoj198 题解 y,z坐标无用. 先化简一下式子,假设选择的是第iii个星球,其x" role="presentation" style="posi ...

  3. UOJ 7 NOI2014 购票

    题意:给一棵树计算一下各个点在距离限制下以一定的费用公式通过不停地到祖先最后到达一号点的最小花费. 第一种做法:线段树维护带修凸壳.显然的,这个公式计算是p*x+q 所以肯定和斜率有关系.然后这题的d ...

  4. 【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra

    [BZOJ3073][Pa2011]Journeys Description Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在 ...

  5. [最短路/线段树大法优化DIJ] 【模板】单源最短路径(标准版)

    洛谷原题 这题我自己看了STL优先队列后试了试优化DIJ算法,但我这个菜比只有32分... 还是老老实实用线段树吧! 自己写的程序,反正AC了,线段树大法好! 具体见代码 #include<bi ...

  6. bzoj5077: [Ctsc2016]时空旅行【线段树+凸包】

    Description 2045年,人类的技术突飞猛进,已经找到了进行时空旅行的方法.小R得到了一台时空旅行仪,他想用它调查不同 时空中人类的发展状况.根据平行时空理论,宇宙中存在着很多独立的时空,每 ...

  7. [CTSC2016]时空旅行(线段树+凸包)

    应该是比较套路的,但是要A掉仍然不容易. 下面理一下思路,思路清楚了也就不难写出来了. 0.显然y,z坐标是搞笑的,忽略即可. 1.如果x不变,那么直接set即可解决. 2.考虑一个空间和询问x0,通 ...

  8. P6773-[NOI2020]命运【线段树合并,树形dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P6773 题目大意 nnn个点的一棵树,边权可以是000或111.mmm个条件(x,y)(x,y)(x,y)表示要求 ...

  9. YJJ's Salesman HDU - 6447(线段树 单点更新+DP思想)

    YJJ's Salesman 题目链接:HDU - 6447 题意:一个1e9*1e9的地图,要求由(0, 0) -> (1e9, 1e9):只能向下,向右, 向右下移动:地图中有n个点,有宝藏 ...

最新文章

  1. Java实现红包随机金额算法
  2. leetcode算法题--链表中倒数第k个节点
  3. WannaCry勒索比特币蠕虫病毒解决方案
  4. The type sun.management.ManagementFactory is not visible
  5. opencv Mat常用操作
  6. 已经到了退休年龄的城乡居民,可以一次性补交十五年的养老金吗?
  7. php 快手视频,初学PHP:简单的快手和抖音短视频解析
  8. mysql 查询每个班级的前三名
  9. excel单元格中换行的办法/word中添加脚注的方法
  10. Python版的BS期权定价模型和希腊值分析
  11. 如何定期清理DNS缓存?清理DNS缓存有什么用?
  12. Spring中Bean生命周期、实例化与初始化
  13. Chrome浏览器首次打开后未响应较长时间
  14. BigSur下Safari14.1.1安装油猴插件(Tampermonkey)
  15. MOOS程序解析记录(6)pLogger
  16. 2 c++编程-核心
  17. 常用SEISMIC BINARY数据的读取
  18. 计算机教学中心理反思,多媒体教学反思
  19. 记我的第一个实用bat编写修改历程(windows聚焦壁纸提取)
  20. ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)

热门文章

  1. java多线程w3c_Java创建多线程的三种方式
  2. mysql 命令源码_MySQL常用命令
  3. 人物关系 人脸识别_原因解密:格里兹曼宣布终止与华为合作,不只是因为人脸识别系统...
  4. token验证失败_ASP.NET CORE WEBAPI JWT 带BEARER的TOKEN
  5. 什么叫计算机网络阻塞,计算机网络体系结构的阻塞控制是什么呢?
  6. 径向基函数神经网络_基于RBF神经网络的网络安全态势感知预测研究
  7. mysql clob blob,如何在MySQL中插入BLOB和CLOB文件?
  8. 一个小型网站多少台服务器,小型企业局域网需要服务器的价格在多少左右(40台机器)...
  9. python多次循环输出_函数的Python循环(多次将输出作为输入重用)
  10. cap mysql_.NetCore关于Cap(RabbitMQ)结合MySql使用出现MySql相关类冲突问题解决办法