题目传送门: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: bzoj 3672: 「NOI2014」购票的更多相关文章

  1. LOJ 2567: 洛谷 P3643: bzoj 4584: 「APIO2016」划艇

    题目传送门:LOJ #2249. 题意简述: 有 \(n\) 个位置,第 \(i\) 个位置可以填在 \([a_i,b_i]\) (\(1\le a_i\le b_i\le 10^9\))之间的整数, ...

  2. LOJ 2085: 洛谷 P1587: bzoj 4652: 「NOI2016」循环之美

    题目传送门:LOJ #2085. 两个月之前做的傻题,还是有必要补一下博客. 题意简述: 求分子为不超过 \(n\) 的正整数,分母为不超过 \(m\) 的正整数的所有互不相等的分数中,有多少在 \( ...

  3. LOJ 2249: 洛谷 P2305: 「NOI2014」购票

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

  4. 「NOI2014」购票 解题报告

    「NOI2014」购票 写完了后发现写的做法是假的...然后居然过了,然后就懒得管正解了. 发现需要维护凸包,动态加点,询问区间,强制在线 可以二进制分组搞,然后你发现在树上需要资瓷撤回,然后暴力撤回 ...

  5. 「NOI2014」购票

    「NOI2014」购票 解题思路 先列出 \(dp\) 式子并稍微转化一下 \[ dp[u] =\min(dp[v]+(dis[u]-dis[v]) \times p[u] + q[u])) \ \ ...

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

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

  7. 洛谷 8 月月赛 & 「PMOI」Round · 04

    T1 T166167 「PMOI-4」人赢 题目大意 给一个数列的前两项分别为\(n\)和\(m\) 当\(i\geq3\)时\(a_i = a_{i-1}*a_{i-2}\)的个位 给定\(n\), ...

  8. LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想

    题目:https://loj.ac/problem/2473 https://www.luogu.org/problemnew/show/P4365 参考:https://blog.csdn.net/ ...

  9. 洛谷 P5206: bzoj 5475: LOJ 2983: [WC2019] 数树

    一道技巧性非常强的计数题,历年WC出得最好(同时可能是比较简单)的题目之一. 题目传送门:洛谷P5206. 题意简述: 给定 \(n, y\). 一张图有 \(|V| = n\) 个点.对于两棵树 \ ...

随机推荐

  1. intellij idea安装教程

    1. 双击ideaIU-12.1.1.exe,点击下一步,安装目录改为d:\Program Files\JetBrains\IntelliJ IDEA,其他项都默认即可(此处更改目录方便重做系统,不用 ...

  2. AtCoder Grand Contest 036 简要题解

    从这里开始 比赛目录 Problem A Triangle 考虑把三角形移到和坐标轴相交,即 然后能够用坐标比较简单地计算面积,简单构造一下就行了. Code #include <bits/st ...

  3. 应用层协议:HTTP

    1. HTTP定义 HyperText Transfer Protocol,超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络 ...

  4. java web开发入门七(mybatis)基于intellig idea

    mybatis 一. 入门开发步骤 1.导入相关jar包 mybatis3.4.2核心开发包 asm-5.1.jarcglib-3.2.4.jarcommons-logging-1.1.3.jarlo ...

  5. .NET Core:Token认证

    现在是WebAPI的时代,你所需要面对的不止是浏览器了,通常会使用Web, WebApp, NativeApp等多种呈现方式.其中诸如Ember,Angular,Backbone之类的前端框架类库正随 ...

  6. 破解Xmind时长

    第一步:必须先进入软件,新建一个思维导图,产生用户状态文件就行. 直接软解: 第二步:打开路径:C:\Users\你的电脑名称\AppData\Roaming\XMind ZEN\Electron v ...

  7. 文件上传之靶场upload-labs (11-20)

    第十一关 strrpos() 函数查找字符串在另一字符串中最后一次出现的位置 substr() 函数返回字符串的一部分 文件保存的方式是上传路径+随机时间+截取的文件后缀 其中上传路径可控,可以利用这 ...

  8. Java ReentrantLock中tryLock与lock的区别(非公平锁与公平锁)

    设置同步状态,利用CAS操作. // CAS操作:如果当前状态值等于期望值,则自动将同步状态设置为给定的更新值 protected final boolean compareAndSetState(i ...

  9. Linux 安装Redis4.0.8【yum安装】

    .下载yum源 yum install epel-release2.安装redisyum install redis3.启动redis # 启动redis service redis start # ...

  10. JavaScriptCore在浏览器引擎中的位置

    因为随着JS这门语言的发展,JS的宿主越来越多,有各种各样的浏览器,甚至是常见于服务端的Node.js(基于V8运行). 2.       Webkit 源代码由三大模块组成: 1). WebCore ...