[NOI2014]购票——斜率优化+树链剖分+线段树
建议到UOJ上去交
题解
一眼\(DP\),先把转移方程写出来
设\(dp[i]\)为从点\(i\)出发到点\(1\)的最小费用,那么存在转移
\]
这个式子看起来可以斜率优化啊,往下化几步,可以得到类似下面的东西:
若\(d[j] < d[k]\),则当\(\frac{dp[j]-dp[k]}{d[j]-d[k]}\geqslant p[i]\)时从\(j\)转移更优,否则从\(k\)转移更优
这表明我们只需要维护一个下凸壳,转移时二分一下就行了
假设这个问题是在序列上且没有距离限制,你就已经可以\(O(nlogn)\)地\(A\)掉它了
加上距离限制时,我们可以拿个线段树维护一下每一小段的凸壳,查询时取个最大值
挪到树上时,只需要上个树剖
托上面两个东西的福,复杂度也变成了\(O(nlog^3n)\)[手动滑稽]
然后就是码码码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200000
#define vi vector<int>
#define pb push_back
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define LIM 17
int n, t;
vi G[MAXN + 5];
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;
ll d[MAXN + 5], s[MAXN + 5], p[MAXN + 5], q[MAXN + 5], l[MAXN + 5], dp[MAXN + 5];
namespace HLD {
void dfs1(int u) {
sz[u] = 1;
f[0][u] = fa[u];
for (int i = 1; i <= LIM; ++i) f[i][u] = f[i - 1][f[i - 1][u]];
for (auto v : G[u]) {
if (v == fa[u]) continue;
d[v] = d[u] + s[v];
dfs1(v);
sz[u] += sz[v];
if (sz[v] > sz[hson[u]]) hson[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++dfn_clk;
id[dfn_clk] = u;
if (hson[u]) dfs2(hson[u], tp);
for (auto v : G[u]) {
if (v == fa[u] || v == hson[u]) continue;
dfs2(v, v);
}
}
}
double slope(int x, int y) {
return static_cast<double>(dp[y] - dp[x])/(d[y] - d[x]);
}
void pre() {
for (int i = 2; i <= n; ++i) {
int u = i;
for (int j = LIM; ~j; --j)
if (d[i] - d[f[j][u]] <= l[i])
u = f[j][u];
summit[i] = !u ? 1 : u;
}
}
namespace SegementTree {
#define mid ((l + r) >> 1)
#define lson (o << 1)
#define rson (o << 1 | 1)
vi ch[4 * MAXN + 5];
void addPoint(vi &v, int x) { // 把点x扔进下凸壳
while (v.size() >= 2 && slope(x, v[v.size() - 2]) < slope(v[v.size() - 1], v[v.size() - 2])) v.pop_back();
v.push_back(x);
}
ll get(vi &v, ll p) { // 在凸壳上二分斜率
if (v.size() == 1) return dp[v[0]] - d[v[0]] * p;
ll ret = INF;
int l = 0, r = v.size() - 1, m;
double s1, s2;
while (l <= r) {
m = (l + r) >> 1;
if (m == v.size() - 1) {
s1 = slope(v[m - 1], v[m]);
ret = min(ret, dp[v[m]] - d[v[m]] * p);
if (s1 <= p) l = m + 1;
else r = m - 1;
}
else if (!m) {
s2 = slope(v[m], v[m + 1]);
ret = min(ret, dp[v[m]] - d[v[m]] * p);
if (s2 <= p) l = m + 1;
else r = m - 1;
}
else {
s1 = slope(v[m - 1], v[m]);
s2 = slope(v[m], v[m + 1]);
ret = min(ret, dp[v[m]] - d[v[m]] * p);
if (s1 <= p && s2 <= p) l = m + 1;
else if(s1 <= p && s2 > p) return min(ret, dp[v[m]] - d[v[m]] * p);
else r = m - 1;
}
}
return ret;
}
void insert(int o, int l, int r, int x, int u) { // 插入一个点
addPoint(ch[o], u);
if (l == r) return ;
if (x <= mid) insert(lson, l, mid, x, u);
else insert(rson, mid + 1, r, x, u);
}
ll query(int o, int l, int r, int L, int R, ll p) { // 在那一堆凸壳中找最小值
if (L <= l && r <= R) return get(ch[o], p);
ll ret = INF;
if (L <= mid) ret = min(ret, query(lson, l, mid, L, R, p));
if (R > mid) ret = min(ret, query(rson, mid + 1, r, L, R, p));
return ret;
}
#undef mid
#undef lson
#undef rson
}
using namespace SegementTree;
void update(int u) { // 求点u的DP值
dp[u] = INF;
int x = fa[u];
while (d[top[x]] >= d[summit[u]]) {
dp[u] = min(dp[u], query(1, 1, n, dfn[top[x]], dfn[x], p[u]) + d[u] * p[u] + q[u]);
x = fa[top[x]];
}
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]);
}
void work(int u) {
if (u != 1) update(u);
insert(1, 1, n, dfn[u], u);
for (auto v : G[u]) {
if (v == fa[u]) continue;
work(v);
}
}
int main() {
scanf("%d%d", &n, &t);
for (int i = 2; i <= n; ++i) {
scanf("%d%lld%lld%lld%lld", &fa[i], &s[i], &p[i], &q[i], &l[i]);
G[fa[i]].pb(i);
}
HLD::dfs1(1);
HLD::dfs2(1, 1);
d[0] = -1;
pre();
work(1);
for (int i = 2; i <= n; ++i) printf("%lld\n", dp[i]);
return 0;
}
[NOI2014]购票——斜率优化+树链剖分+线段树的更多相关文章
- BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)
前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...
- 【bzoj2402】陶陶的难题II 分数规划+树链剖分+线段树+STL-vector+凸包+二分
题目描述 输入 第一行包含一个正整数N,表示树中结点的个数.第二行包含N个正实数,第i个数表示xi (1<=xi<=10^5).第三行包含N个正实数,第i个数表示yi (1<=yi& ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- BZOJ2243 (树链剖分+线段树)
Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...
- POJ3237 (树链剖分+线段树)
Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...
- bzoj4034 (树链剖分+线段树)
Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...
- HDU4897 (树链剖分+线段树)
Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...
- 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 ...
- 【POJ3237】Tree(树链剖分+线段树)
Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...
随机推荐
- 最新 网宿科技java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.网宿科技等10家互联网公司的校招Offer,因为某些自身原因最终选择了网宿科技.6.7月主要是做系统复习.项目复盘.Leet ...
- Js 集合运用
1.给定一个单词good 要求输入 g1 o2 d1 (字母+加字母个数) 方法一: <script type="text/javascript"> var arrO ...
- shell 入门学习
目录 shell 入门学习 注释 执行与启动 变量 语法 调试 title: shell 入门学习 date: 2019/7/16 15:47:49 toc: true --- shell 入门学习 ...
- MongoDB的可视化工具(Studio 3T)的安装
通过可视化工具,我们可以不用在命令行操作MongoDB了,可以直接使用类似于SQLyog的软件进行相关操作 1.下载地址(二者择其一即可),可使用迅雷来进行下载,否则下载速度极其慢 2018.4.6版 ...
- (十一)springMvc 异常处理
文章目录 思路 自定义异常处理类 全局异常处理器 配置全局异常处理器 思路 在 springMvc 中,异常一层一层的往上抛,最后抛给 前端控制器,前端控制器内部会去找 全局异常处理器(一个系统只会有 ...
- (四)循环队列 VS 数组队列 (效率对比)
目录 背景 测试代码 结果 链表 随机访问 背景 各自完成插入 10万.20万 条随机数,然后再将这些随机数出队列 : 测试代码 /** * 测试速度 */ public String testSpe ...
- 连续取数字DP使值最大HDU2697
题意: 有n个数,每个数都有价钱,连续的取可以获得len*len的利益,使利益最大. 思路: 三维DP,1.2.3维分别是第i个,剩余多少钱,从后往前连续的有几个. #define IOS ios_b ...
- php 处理图片 将图片转成base64
1.直接将图片路径传入下面该方法就可以了//将图片转成base64 public function imgToBase64($img_file){ $img_base64 = ''; if ($img ...
- nginx浏览器开启密码验证
如果我们在 nginx 下搭建了一些站点,但是由于站点内容或者流量的关系,我们并不想让所有人都能正常访问,那么我们可以设置访问认证.只有让用户输入正确的用户名和密码才能正常访问.效果如下: 在 ngi ...
- Your ApplicationContext is unlikely tostart due to a @ComponentScan of the defau
一.错误提示: Your ApplicationContext is unlikely tostart due to a @ComponentScan of the default package.. ...