题目传送门:LOJ #2249

题意简述:

有一棵以 \(1\) 号节点为根节点的带边权的树。

除了 \(1\) 号节点的所有节点上都有人需要坐车到达 \(1\) 号节点。

除了 \(1\) 号节点,每个节点都有 \(5\) 个参数 \(f_u,s_u,p_u,q_u,l_u\)。

\(f_u\) 表示 \(u\) 号点的父亲,\(s_u\) 表示 \(u\) 号点与父亲之间的边的权值,\(p,q,l\) 为车票参数。

定义两个节点 \(u\) 和 \(v\) 之间的距离 \(dis_{u,v}\) 为 \(u\) 和 \(v\) 在树上的简单路径上的边权之和。

从一个节点 \(u\),可以坐车到达节点 \(v\) 当且仅当 \(dis_{u,v}\le l_u\),且需要花费 \(dis_{u,v}\times p_u+q_u\) 的代价。

问除了 \(1\) 号节点的每个节点坐车到 \(1\) 号节点需要花费的最小总代价。

\(1\le n\le 2\times 10^5\),保证输入的所有数都是非负整数,\(l_u\ge s_u\),保证答案在 long long 范围内。

题解:

考虑树形 DP,令 \(\mathrm{f}[u]\) 为 \(u\) 到 \(1\) 的最小总代价,\(\mathrm{dis}[u]\) 为 \(1\) 号节点到 \(u\) 号节点的距离。

则有 \(\mathrm{f}[u]=\min\limits_{\substack{v\in anc_u\\\mathrm{dis}[u]-\mathrm{dis}[v]\le l_u}}(\mathrm{f}[v]+(\mathrm{dis}[u]-\mathrm{dis}[v])\times p_u+q_u)\)。

令 \(b_u=\mathrm{dis}[u]\times p_u+q_u\),则有:

\[\mathrm{f}[u]=b_u+\min\limits_{\substack{v\in anc_u\\\mathrm{dis}[u]-\mathrm{dis}[v]\le l_u}}(\mathrm{f}[v]-\mathrm{dis}[v]\times p_u)
\]

观察式子,发现 \(v\) 必须是紧接着 \(u\) 往上一段连续的祖先,而转移的方式是简单的加权值取 \(\min\)。

这提示我们考虑斜率优化,考虑两个合法转移点 \(j\) 和 \(k\),比较 \(j\) 和 \(k\) 转移的优劣:

\[\begin{aligned}\mathrm{f}[j]-\mathrm{dis}[j]\times p_u&\Leftrightarrow\mathrm{f}[k]-\mathrm{dis}[k]\times p_u\\\mathrm{f}[k]-\mathrm{f}[j]&\Leftrightarrow(\mathrm{dis}[k]-\mathrm{dis}[j])\times p_u\end{aligned}
\]

发现已经有了斜率的表达式,\(x\) 坐标是 \(\mathrm{dis}\),\(y\) 坐标是 \(\mathrm{f}\)。假设 \(\mathrm{dis}[j]<\mathrm{dis}[k]\),则决策 \(j\) 比 \(k\) 优当且仅当:

\[\begin{aligned}\mathrm{f}[j]-\mathrm{dis}[j]\times p_u&<\mathrm{f}[k]-\mathrm{dis}[k]\times p_u\\\mathrm{f}[k]-\mathrm{f}[j]&>(\mathrm{dis}[k]-\mathrm{dis}[j])\times p_u\\\frac{\mathrm{f}[k]-\mathrm{f}[j]}{\mathrm{dis}[k]-\mathrm{dis}[j]}&>p_u\end{aligned}
\]

即点 \((\mathrm{dis}[j],\mathrm{f}[j])\) 和点 \((\mathrm{dis}[k],\mathrm{f}[k])\) 之间的线段的斜率大于 \(p_u\)。

所以我们需要用单调栈维护一个合法点的下凸壳,这样可以根据不同的 \(p_u\) 在凸壳上二分得到决策点。

但是这样有一个问题,我们知道合法的决策点是 \(u\) 的近几层祖先,即 \(u\) 到祖先的链上 \(\mathrm{dis}\) 较大的节点,它们对应的点也是靠右的。

考虑这张图,假设 A 点是非法转移点,而 B、C、D 都是合法转移点,此时维护的凸壳并不包含 B,然而 B 可能是最优决策点。

也就是说,我们需要考虑一段后缀中的最优转移点,然而我们又不能维护所有后缀的凸壳,应该怎么办呢?

有一个解决方法:维护一些区间中的凸壳,查询时选择一些区间的并恰好等于需要的后缀,在每个区间内分别查询最优决策并更新。

这提示我们使用树状数组维护后缀信息,也就是树状数组套单调栈。

这样花费的空间是 \(\Theta(n\log n)\) 的,修改时间 \(\mathcal{O}(\log n)\),查询时间 \(\mathcal{O}(\log^2 n)\)。

还有最后一个问题,需要处理的不是序列上而是树上的转移,这并不难实现,我们维护可撤销单调栈,在回溯的时候撤销这次操作即可。

关于可撤销单调栈,考虑单调栈的每一次操作只会更改栈顶指针以及栈顶位置的值,我们维护历史每一次操作原先的栈顶指针以及栈顶位置原来的值即可实现撤销操作,这样插入和撤销均是 \(\Theta(1)\) 的。

#include <cstdio>
#include <vector>
#include <algorithm> typedef long long LL;
const int MN = 200005;
const int MS = 2100005;
const LL Inf = 0x7fffffffffffffff; int N, faz[MN];
std::vector<int> G[MN];
LL wgh[MN], dep[MN], dis[MN], p[MN], q[MN], b[MN], len[MN];
LL stds[MN], tds;
LL f[MN], *X = dis, *Y = f;
inline double Slope(int i, int j) {
return X[i] == X[j] ? 1e60 : (double)(Y[j] - Y[i]) / (X[j] - X[i]);
} int stkp[MS], valp[MS], tpp[MS];
int *istk[MN], *ival[MN], *itp[MN], it[MN];
inline void Push(int id, int u) {
int t = it[id], tp = itp[id][t], *stk = istk[id];
while (tp > 0 && Slope(stk[tp - 1], stk[tp]) >= Slope(stk[tp], u)) --tp;
++t, ival[id][t] = stk[itp[id][t] = ++tp], stk[tp] = u, it[id] = t;
}
inline void Pop(int id) {
istk[id][itp[id][it[id]]] = ival[id][it[id]];
--it[id];
}
inline int chk(int id, LL slp) {
int t = it[id], tp = itp[id][t], *stk = istk[id];
if (!~tp) return -1;
int lb = 0, rb = tp - 1, x = tp, mid;
while (lb <= rb) {
mid = (lb + rb) >> 1;
if (Slope(stk[mid], stk[mid + 1]) <= slp) lb = mid + 1;
else x = mid, rb = mid - 1;
}
return stk[x];
}
inline void PushAll(int i, int u) { for (; i <= N; i += i & -i) Push(i, u); }
inline void PopAll(int i) { for (; i <= N; i += i & -i) Pop(i); }
inline void GetDp(int u) {
f[u] = Inf;
int i = std::lower_bound(stds + 1, stds + tds + 1, dis[u] - len[u]) - stds;
for (i = N - i + 1; i; i -= i & -i) {
int v = chk(i, p[u]);
if (~v) f[u] = std::min(f[u], f[v] - dis[v] * p[u] + b[u]);
}
} void DFS(int u) {
dep[u] = dep[faz[u]] + 1;
dis[u] = dis[faz[u]] + wgh[u];
stds[++tds] = dis[u];
b[u] = dis[u] * p[u] + q[u];
GetDp(u);
PushAll(N - dep[u], u);
for (auto v : G[u]) DFS(v);
PopAll(N - dep[u]);
--tds;
} int main() {
scanf("%d%*d", &N);
istk[1] = stkp, itp[1] = tpp, ival[1] = valp, itp[1][it[1] = 0] = -1;
for (int i = 2; i <= N; ++i) {
int lenl = ((i - 1) & (1 - i)) + 1;
istk[i] = istk[i - 1] + lenl;
itp[i] = itp[i - 1] + lenl;
ival[i] = ival[i - 1] + lenl;
itp[i][it[i] = 0] = -1;
}
for (int i = 2; i <= N; ++i) {
scanf("%d%lld%lld%lld%lld", &faz[i], &wgh[i], &p[i], &q[i], &len[i]);
G[faz[i]].push_back(i);
}
PushAll(N, 1), stds[tds = 1] = 0;
for (auto u : G[1]) DFS(u);
for (int i = 2; i <= N; ++i) printf("%lld\n", f[i]);
return 0;
}

LOJ 2249: 洛谷 P2305: 「NOI2014」购票的更多相关文章

  1. LOJ 2249: 洛谷 P2305: bzoj 3672: 「NOI2014」购票

    题目传送门:LOJ #2249. 题意简述: 有一棵以 \(1\) 号节点为根节点的带边权的树. 除了 \(1\) 号节点的所有节点上都有人需要坐车到达 \(1\) 号节点. 除了 \(1\) 号节点 ...

  2. LOJ#2249 Luogu P2305「NOI2014」购票

    几乎肝了半个下午和整个晚上 斜率优化的模型好多啊... LOJ #2249 Luogu P2305 题意 给定一棵树,第$ i$个点如果离某个祖先$ x$的距离不超过$ L_i$,可以花费$ P_i· ...

  3. LOJ 3045: 洛谷 P5326: 「ZJOI2019」开关

    题目传送门:LOJ #3045. 题意简述 略. 题解 从高斯消元出发好像需要一些集合幂级数的知识,就不从这个角度思考了. 令 \(\displaystyle \dot p = \sum_{i = 1 ...

  4. LOJ 3089: 洛谷 P5319: 「BJOI2019」奥术神杖

    题目传送门:LOJ #3089. 题意简述: 有一个长度为 \(n\) 的母串,其中某些位置已固定,另一些位置可以任意填. 同时给定 \(m\) 个小串,第 \(i\) 个为 \(S_i\),所有位置 ...

  5. LOJ 3093: 洛谷 P5323: 「BJOI2019」光线

    题目传送门:LOJ #3093. 题意简述: 有 \(n\) 面玻璃,第 \(i\) 面的透光率为 \(a\),反射率为 \(b\). 问把这 \(n\) 面玻璃按顺序叠在一起后,\(n\) 层玻璃的 ...

  6. LOJ 3043: 洛谷 P5280: 「ZJOI2019」线段树

    题目传送门:LOJ #3043. 题意简述: 你需要模拟线段树的懒标记过程. 初始时有一棵什么标记都没有的 \(n\) 阶线段树. 每次修改会把当前所有的线段树复制一份,然后对于这些线段树实行一次区间 ...

  7. LOJ 2483: 洛谷 P4655: 「CEOI2017」Building Bridges

    题目传送门:LOJ #2483. 题意简述: 有 \(n\) 个数,每个数有高度 \(h_i\) 和价格 \(w_i\) 两个属性. 你可以花费 \(w_i\) 的代价移除第 \(i\) 个数(不能移 ...

  8. LOJ 2312(洛谷 3733) 「HAOI2017」八纵八横——线段树分治+线性基+bitset

    题目:https://loj.ac/problem/2312 https://www.luogu.org/problemnew/show/P3733 原本以为要线段树分治+LCT,查了查发现环上的值直 ...

  9. 洛谷 P4710 「物理」平抛运动

    洛谷 P4710 「物理」平抛运动 洛谷传送门 题目描述 小 F 回到班上,面对自己 28 / 110 的物理,感觉非常凉凉.他准备从最基础的力学学起. 如图,一个可以视为质点的小球在点 A(x_0, ...

随机推荐

  1. PAT-1001 采花生

    题目描述 鲁宾逊先生有一只宠物猴,名叫多多.这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!——熊字”. 鲁宾逊先生和多多都很开心,因为花生正是他 ...

  2. Beta 冲刺 六

    团队成员 051601135 岳冠宇 031602629 刘意晗 031602248 郑智文 031602330 苏芳锃 031602234 王淇 照片 项目进展 岳冠宇 昨天的困难 ActionBa ...

  3. 课堂Beta发布

    项目组名:奋斗吧兄弟 小组成员:黄兴,李俞寰,栾骄阳,王东涵,杜桥 今天6个小组在课上进行了Bate发布,以下是我的一些看法: 飞天小女警的礼物挑选系统: 由于是第一个Bate发布的项目,所以我印象较 ...

  4. 【iMooc】全面解析java注解

    在慕课上学习了一个关于java注解的课程,下面是笔记以及一些源码. Annotation——注解 1.JDK中的注解 JDK中包括下面三种注解: @Override:标记注解(marker annot ...

  5. laravel 登录后跳转原来浏览的页面

    方法 1.修改一下文件/vendor/laravel/framework/src/Illuminate/Foundation/Auth/RedirectsUsers.php 修改内容如下: 没有的加入 ...

  6. thnkphp框架面试问题

    Thinkphp面试问题 1.如何理解TP中的单一入口文件? 答:ThinkPHP采用单一入口模式进行项目部署和访问,无论完成什么功能,一个项目都有一个统一(但不一定是唯一)的入口.应该说,所有项目都 ...

  7. SpringBoot(十四)_springboot使用内置定时任务Scheduled的使用(一)

    为什么使用定时? 日常工作中,经常会用到定时任务,比如各种统计,并不要求实时性.此时可以通过提前设置定时任务先把数据跑出来,后续处理起来更方便. 本篇文章主要介绍 springboot内置定时任务. ...

  8. Delphi7如何实现让Tedit显示文字垂直居中(上下居中)

    通过下面的组件,可以在输入文字的时候自动垂直居中 直接把下面代码保存到Unit1.pas即可------------------------------------------ unit Unit1; ...

  9. Spring注解开发简要步骤

    1.除spring基本包外还需要下载AOP包 spring-aop-4.2.4.RELEASE.jar 2.导入约束(最后两行) <beans xmlns="http://www.sp ...

  10. Java之可变参数方法使用说明

    代码: package test_demo; /* * 可变参数函数说明 * 传入指定数据类型的数组 * 优先匹配固定长度函数 * */ public class VarArgsDemo { // 可 ...