Problem

Background

\(C\) 国拥有一张四通八达的高速公路网树,其中有 \(n\) 个城市,城市之间由一共 \(n-1\) 条高速公路连接。除了首都 \(1\) 号城市,每个城市都有一家本地的客运公司,可以发车前往全国各地,有若干条高速公路连向其他城市,这是一个树型结构,\(1\) 号城市(首都)为根。假设有一个人要从 \(i\) 号城市坐车出发前往 \(j\) 号城市,那么他要花费 \(P_i\)*(\(i\) 城市到 \(j\) 城市的距离)\(+Q_i\) 元。由于距离首都越远,国家的监管就越松,所以距离首都越远,客运公司的 \(P_i\)(单位距离价格)越大,形式化的说,如果把高速路网看成一棵以首都为根的有根树,\(i\) 号城市是 \(j\) 号城市的某个祖先,那么一定存在 \(P_i\le P_j\)。

Description

大宁成为了国家统计局的调查人员,他需要对现在的高速路网进行一次调查,了解从其他每一个城市到达首都1号城市所花费的金钱(路径必须是简单路径)。

因为有非常多转车(或不转车)的抵达首都的方法,所以人工计算这个结果是十分复杂的。大宁非常的懒,所以请你编写一个程序解决它。

Input Format

第 \(1\) 行包含 \(1\) 个非负整数 \(n\),表示城市的个数。

第 \(2\) 到 \(n\) 行,每行描述一个除首都之外的城市。其中第 \(i\) 行包含 \(4\) 个非负整数 \(F_i,S_i,P_i,Q_i\),分别表示 \(i\) 号城市的父亲城市,它到父亲城市高速公路的长度,以及乘车价格的两个参数。

Output Format

输出包含 \(n-1\) 行,每行包含一个整数。

其中第 \(i\) 行表示从 \(i+1\) 号城市 出发,到达首都最少的乘车费用。

Sample

Input 1

6
1 9 3 0
1 17 1 9
1 1 1 6
4 13 2 15
4 9 2 4

Output 1

27
26
7
43
24

Input 2 and Output 2

百度网盘

Range

对于前 \(40\%\) 的数据 \(1\le n\le1000\)。

对于另外 \(20\%\) 的数据 满足从第 \(i\)(\(i\neq1\))个城市出发的高速公路连向第 \(i-1\) 个城市。

对于所有的数据 \(1\le n\le1000000,0\le P_i,Q_i\le2^{31}–1\),保证结果不会大于 \(2^{63}–1\)。

Algorithm

\(DP\),斜率优化

Mentality

看看这个题目,其实一下子就能想到我们可以在每一条从根节点到叶子节点的路径上线性 \(DP\) 。

那么方程也异常简单:设 \(f[i]\) 为从 \(i\) 点到达根节点所需最小费用,\(pre_i\) 为 \(i\) 到根节点路径上所有点的集合,\(lj[i]\) 为 \(i\) 到根节点的距离,那么自然有:

\[f[i]=Min_{j\in pre_i}(f[j]+(lj[i]-lj[j])*P[i]+Q[i])
\]

当然,这个 \(DP\) 方程的复杂度是不过关的。按照一贯的套路,这种很容易想式子的 \(DP\) 题总满足决策单调性。所以我们尝试一下列出它的决策之间有什么关系。

设 \(i\) 的两个决策点为 \(j,k\) ,其中 \(j\in pre_k\) ,\(j,k\in pre_i\) 。

如果从点 \(k\) 转移比从点 \(j\) 转移更优,那么自然有:

\[f[k]+(lj[i]-lj[k])*P[i]+Q[i]<f[j]+(lj[i]-lj[j])*P[i]+Q[i]\\
f[k]-lj[k]*P[i]<f[j]-lj[j]*P[i]\\
f[k]-f[j]<(lj[k]-lj[j])*P[i]\\
\frac{f[k]-f[j]}{lj[k]-lj[j]}<P[i]
\]

我们发现,由于题目有 \(P_i>P_{pre_i}\) 的条件,那么当我们在一条链上 \(DP\) 时,一旦从某一点开始满足上式,则这个点的子树内所有点一直都会满足上式。换句话说,对于这个点的子树内所有点而言,从 \(k\) 转移总会比从 \(j\) 转移更优

换而言之,此题满足决策单调性中的斜率优化的条件。

那么由于 \(P[i]\) 递增,我们只需要维护一个斜率单调递增的决策点队列即可。

但是这是一棵树,怎么办?

很简单,每个点在更改决策点的单调队列时只有三个操作:

  • 不断 \(++head\) ,直到队首斜率不满足上式
  • 不断 \(--tail\) ,直到对尾斜率符合单调性
  • \(++tail\) ,在队列尾部插入当前决策点

那么我们发现,每到一个新的点,其实我们只是修改了一下 \(head\) 和 \(tail\) 的值,并修改了队列中的一个元素。则我们只需要记住修改之前的那个元素的值,修改之前的队首对尾的位置即可,当我们退出当前点的时候,只需要照着之前记录的值改回去就好了!

但是对于这题还不够。

一般而言,我们的斜率优化总会是 \(O(n)\) 的,因为每个点最多只会被入队出队一次,所以总体复杂度只会取决于决策点个数而不是枚举决策点。

但是在这题里,一个决策点可能在这个点里入队,在下一个点里出队,而回溯回来的时候又再次入队,因而不符合每个点最多只会入队出队一次的规则!,换句话说,这样做的话,复杂度是错误的。

那接下来又怎么做呢?

由于我们的决策点队列里,相邻两点之间的斜率是单调的,所以我们完全可以通过二分来找到应该 \(++head,--tail\) 所达到的位置!

只需要找到最左边的,不满足斜率不等式的位置,以及最右边的,不满足斜率单调递增的位置即可。

当然,二分可能有点小细节,可以看看我的代码。(良心注释)

Code

#include <cstdio>
#include <iostream>
using namespace std;
int n;
int cntr, head[1000001], to[1000001], nx[1000001], w[1000001];
int Q[1000001];
int que[1000001], h, t;
long long P[1000001], lj[1000001], f[1000001];
void addr(int u, int v, int W) {
cntr++;
to[cntr] = v, nx[cntr] = head[u], w[cntr] = W;
head[u] = cntr;
}
double slope(int a, int b) {
return (double)(f[b] - f[a]) / (lj[b] - lj[a]);
} //斜率不等式
void DP(int x) {
int p, tph = h, tpt = t, tpw, l = h + 1, r = t,
ans = h; // l 的初值为 h+1,因为我们用来比较的是 mid-1 与 mid
// 两个位置的斜率。之所以不用 l=h,r=t-1 作为初值并比较 mid 与
// mid+1 ,是因为根据我们做斜率题的时候 h
// 总会在最右边的符合不等式的位置的右一个。这样二分出來的位置刚刚好;ans
// 的初值为 h,因为如果找不到相应的位置,那就不需要修改 h。
while (l <= r) {
int mid = (l + r) >> 1;
if (slope(que[mid - 1], que[mid]) <= P[x])
l = mid + 1, ans = mid;
else
r = mid - 1;
}
h = ans, p = que[h];
f[x] = f[p] + (lj[x] - lj[p]) * P[x] + Q[x];
l = h, r = t - 1, ans = t; //这里的理由与队首差不多,都是为了结果正确性
while (l <= r) {
int mid = (l + r) >> 1;
if (slope(que[mid], que[mid + 1]) >= slope(que[mid + 1], x))
r = mid - 1, ans = mid;
else
l = mid + 1;
}
t = ans;
tpw = que[++t], que[t] = x; //记录即将被更改的位置的元素并更新
for (int i = head[x]; i; i = nx[i]) {
lj[to[i]] = lj[x] + w[i]; //更新路径长度
DP(to[i]);
}
que[t] = tpw;
h = tph, t = tpt; //修改回进入这个点之前的状态
}
int main() {
freopen("3994.in", "r", stdin);
freopen("3994.out", "w", stdout);
cin >> n;
int F, W;
for (int i = 2; i <= n; i++) {
scanf("%d%d%lld%d", &F, &W, &P[i], &Q[i]);
addr(F, i, W);
}
t = -1; //最开始队列是空的,所以 tail=head-1
DP(1);
for (int i = 2; i <= n; i++) printf("%lld\n", f[i]);
}

【Luogu P3994】高速公路的更多相关文章

  1. 洛谷 P3994 高速公路

    https://www.luogu.org/problemnew/show/P3994 设dp[i] 表示第i个城市到根节点的最小花费 dp[i]=min{ (dis[i]-dis[j])*P[i]+ ...

  2. P3994 高速公路 树形DP+斜率优化+二分

    $ \color{#0066ff}{ 题目描述 }$ C国拥有一张四通八达的高速公路网树,其中有n个城市,城市之间由一共n-1条高速公路连接.除了首都1号城市,每个城市都有一家本地的客运公司,可以发车 ...

  3. 洛谷 P3994 高速公路(斜率优化)

    题目链接 题意:给出一棵树,\(1\) 号点为根,边上有边权. 每个点有两个参数 \(p_i,q_i\) 如果你想从 \(i\) 号点到与其距离为 \(d\) 的 \(j\) 号点,那么你需花费 \( ...

  4. P3994 高速公路

    题目链接 题意分析 这是一道树上斜率优化题 首先 \[dp[i]=min\{dp[j]+(dis[i]-dis[j])* p[i]+q[i]\}(j∈Pre_i)\] 那么就是 \[p[i]=\fra ...

  5. [Luogu 2221] HAOI2012 高速公路

    [Luogu 2221] HAOI2012 高速公路 比较容易看出的线段树题目. 由于等概率,期望便转化为 子集元素和/子集个数. 每一段l..r中,子集元素和为: \(\sum w_{i}(i-l+ ...

  6. 【题解】Luogu P2221 [HAOI2012]高速公路

    原题传送门 这道题还算简单 我们要求的期望值: \[\frac{\sum_{i=l}^r\sum_{j=l}^rdis[i][j]}{C_{r-l+1}^{2}}\] 当然是上下两部分分别求,下面肥肠 ...

  7. 【Luogu】P2221高速公路(线段树乱搞)

    题目链接 这题……我从一开始就想歪了qwq. 为了缅怀逝去的一小时我就把我的30分暴力思路放上来. 首先我们观察枚举的区间.假设我们要枚举的范围是1~5之间的四条路,为了方便我们把它们叫做abcd. ...

  8. luogu P2221 [HAOI2012]高速公路题解

    题面 很套路的拆式子然后线段树上维护区间和的题.一般都是把式子拆成区间内几个形如\(\sum i*a_i, \sum i^2 * a_i\)的式子相加减的形式. 考虑一次询问[l,r]的答案怎么算: ...

  9. 【BZOJ2752】【Luogu P2221】 [HAOI2012]高速公路

    不是很难的一个题目.正确思路是统计每一条边被经过的次数,但我最初由于习惯直接先上了一个前缀和再推的式子,导致极其麻烦难以写对而且会爆\(longlong\). 推导过程请看这里. #include & ...

随机推荐

  1. 【玩转SpringBoot】看似复杂的Environment其实很简单

    喜欢写代码,讨厌配环境 我相信这十个字的小标题代表了大多数码农的心声. 十年前读大学时,学校开设了C语言还有C++.但是学习这两种语言,对于新手来说非常没有成就感. 于是我就在校门口买个光盘,装个VS ...

  2. elasticsearch的快速安装

    在阿里云服务器快速安装ElasticSearch 1.安装好java的jdk环境 2.使用wget下载elasticsearch安装包,wget的速度比较满,如果等不及的话,可以先下载好安装包再上传解 ...

  3. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

  4. Android 插件化开发(三):资源插件化

    在前面的文章中我们成功的加载了外部的Dex(Apk)并执行了插件的Bean代码.这时我们会想,能不能加载并运行插件Apk的Activity.答案当然是能,否则后续我们的研究就没意义了,但是想实现Act ...

  5. Cookie与Session会话技术

    Cookie与Session会话技术 一.什么是会话 会话:当用户打开浏览器,访问多个WEB资源,然后关闭浏览器的过程,称之为一个会话,选项卡,弹出页面都属于这个会话,且共享同一个session. 二 ...

  6. C语言搬书学习第一记 —— 认识一个简单程序的细节

    #include<stdio.h> /*告诉编译器把stdio.h 中的内容包含在当前程序中,stdio.h是C编译器软件包的标准部分,它提供键盘输入和 屏幕输入的支持studio.h文件 ...

  7. 流程图软件 drawio 免费 github开源

    做程序需要画流程图,发现迅捷流程图的在线版挺好用的,但是,它的导出只允许VIP会员,不是VIP会员只能导出xsd文件,而且要注册账号,极为麻烦. 在知乎看到了一位网友的评论,有一款软件和迅捷流程图一模 ...

  8. 线程提供的方法:static void sleep(long ms),会进入阻塞状态,休眠

    package seday08.thread; import java.util.Scanner; /*** @author xingsir * 线程提供的方法:static void sleep(l ...

  9. 2.Ansible Playbook剧本

    1.playbook?playbook翻译过来就是"剧本",那playbook组成如下 play: 定义的是主机的角色 task: 定义的是具体执行的任务 playbook: 由一 ...

  10. Springboot vue.js html 跨域 前后分离 Activiti6 工作流 集成代码生成器 shiro 权限

    官网:www.fhadmin.org 特别注意: Springboot 工作流  前后分离 + 跨域 版本 (权限控制到菜单和按钮) 后台框架:springboot2.1.2+ activiti6.0 ...