【Luogu P3994】高速公路
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\) 到根节点的距离,那么自然有:
\]
当然,这个 \(DP\) 方程的复杂度是不过关的。按照一贯的套路,这种很容易想式子的 \(DP\) 题总满足决策单调性。所以我们尝试一下列出它的决策之间有什么关系。
设 \(i\) 的两个决策点为 \(j,k\) ,其中 \(j\in pre_k\) ,\(j,k\in pre_i\) 。
如果从点 \(k\) 转移比从点 \(j\) 转移更优,那么自然有:
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】高速公路的更多相关文章
- 洛谷 P3994 高速公路
https://www.luogu.org/problemnew/show/P3994 设dp[i] 表示第i个城市到根节点的最小花费 dp[i]=min{ (dis[i]-dis[j])*P[i]+ ...
- P3994 高速公路 树形DP+斜率优化+二分
$ \color{#0066ff}{ 题目描述 }$ C国拥有一张四通八达的高速公路网树,其中有n个城市,城市之间由一共n-1条高速公路连接.除了首都1号城市,每个城市都有一家本地的客运公司,可以发车 ...
- 洛谷 P3994 高速公路(斜率优化)
题目链接 题意:给出一棵树,\(1\) 号点为根,边上有边权. 每个点有两个参数 \(p_i,q_i\) 如果你想从 \(i\) 号点到与其距离为 \(d\) 的 \(j\) 号点,那么你需花费 \( ...
- P3994 高速公路
题目链接 题意分析 这是一道树上斜率优化题 首先 \[dp[i]=min\{dp[j]+(dis[i]-dis[j])* p[i]+q[i]\}(j∈Pre_i)\] 那么就是 \[p[i]=\fra ...
- [Luogu 2221] HAOI2012 高速公路
[Luogu 2221] HAOI2012 高速公路 比较容易看出的线段树题目. 由于等概率,期望便转化为 子集元素和/子集个数. 每一段l..r中,子集元素和为: \(\sum w_{i}(i-l+ ...
- 【题解】Luogu P2221 [HAOI2012]高速公路
原题传送门 这道题还算简单 我们要求的期望值: \[\frac{\sum_{i=l}^r\sum_{j=l}^rdis[i][j]}{C_{r-l+1}^{2}}\] 当然是上下两部分分别求,下面肥肠 ...
- 【Luogu】P2221高速公路(线段树乱搞)
题目链接 这题……我从一开始就想歪了qwq. 为了缅怀逝去的一小时我就把我的30分暴力思路放上来. 首先我们观察枚举的区间.假设我们要枚举的范围是1~5之间的四条路,为了方便我们把它们叫做abcd. ...
- luogu P2221 [HAOI2012]高速公路题解
题面 很套路的拆式子然后线段树上维护区间和的题.一般都是把式子拆成区间内几个形如\(\sum i*a_i, \sum i^2 * a_i\)的式子相加减的形式. 考虑一次询问[l,r]的答案怎么算: ...
- 【BZOJ2752】【Luogu P2221】 [HAOI2012]高速公路
不是很难的一个题目.正确思路是统计每一条边被经过的次数,但我最初由于习惯直接先上了一个前缀和再推的式子,导致极其麻烦难以写对而且会爆\(longlong\). 推导过程请看这里. #include & ...
随机推荐
- django----session相关配置
1. 数据库Session SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2. 缓存Session SESSION_E ...
- nginx代理grafana
希望通过Nginx为服务器上的grafana进行代理,实现通过在当前域名后加/grafana在公网进行访问,开始按照百度的方法弄了几个小时都不行,后面仔细看了官方的文档才弄好,Mark一下. Ngin ...
- 更新Preloader和uboot
在SoCEDS环境下编译和更新preloader和uboot程序的方法 前面有介绍preloader在HPS boot过程中的的作用,接下来讲述下用户在SoCEDS环境下改如何编译preloade ...
- 分布式事物 - 基于RPC调用 - 补偿模式
前提 所有服务均有独立的事物管理机制,相互间没有任何关联. 所有业务接口都有对应的补偿方法,用于将已经更新的数据还原到上一次的状态. 本次实例为同步业务,理想状态下,只有全部成功或全部失败两种情况. ...
- Hack the De-ICE: S1.120 VM (Boot to Root)
下载地址: https://www.vulnhub.com/entry/de-ice-s1120,10/ 静态IP:192.168.1.120 主机扫描: ╰─ nmap -p1-65535 -sV ...
- Linux Pptpd
准备环境 1 主机ip:192.168.0.107 2 VPN服务器: ens32:192.168.0.102 ens33:127.16.1.10 软件安装 [root@localhost ~]# y ...
- docker学习笔记---基本命令
[root@docker ~]# docker Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Opt ...
- 安装Android Studio (一)同时配置Android Studio环境变量
安装Android Studio 安装android 一直next就行了.这以步需要10分钟左右. 我的安卓SDK在这个目录 C:\Users\user\AppData\Local\Android\s ...
- 一起学SpringMVC之异常处理
在系统开发过程中,异常处理是不可避免,如果异常处理不好,会给用户造成很差的体验,本文主要讲解在SpringMVC开发过程中,异常处理的相关知识点,仅供学习分享使用,如有不足之处,还请指正. 概述 在S ...
- (转)LSTM神经网络介绍
原文链接:http://www.atyun.com/16821.html 扩展阅读: https://machinelearningmastery.com/time-series-prediction ...