洛谷3994高速公路

题目背景

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

题目描述

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

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

输入输出样例

输入样例#1:

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

输出样例#1:

27
26
7
43
24

说明

对于前40%的数据1<=n<=1000。

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

对于所有的数据1<=n<=1000000,0<=Pi,Qi<=231-1,保证结果不会大于263-1。

思路:

​ 这道题我们可以设S[i]代表首都到i城市的距离, dp[i]代表第i个城市到首都最少乘车费用,那么我们可以得到递推式:

dp[i] = min(dp[j] + P[i] * (S[i] - S[j]) + Q[i]) (0 <= j < i)

​ 上面这个式子的时间复杂度为O(n ^ 2),由于n的范围为1e6,因此我们必须优化为O(n)的做法,我们发现,该式符合斜率优化的特征:

dp[j]为变量,P[i] * S[i]和Q[i]为不变量,而P[i]*S[j]为混合量

​ 我们开始将混合量去掉:

第一步:由于该题所有城市组成一棵树,1号首都为根结点。我们设j 为 k的子节点(不一定是直接子节点)且对于求dp[i],j比k优,那么:

dp[j] + P[i] * (S[i] - S[j]) + Q[i] < dp[k] + P[i] * (S[i] - S[k]) + Q[i]
dp[j] - P[i] * S[j] < dp[k] - P[i] * S[k]
(dp[j] - dp[k]) / (S[j] - S[k]) < P[i]

​ 其中(dp[j] - dp[k]) / (S[j] - S[k])就为求解的斜率

第二步

​ 设P(p, q)为点p和点q之间的斜率,那么假设j, p, q都是i的祖宗节点且q是j的祖宗节点,p是q的祖宗节点且P(p, q) > P(q, j)。我们讨论所有的情况

(1) P[i] < P(q, j) < P(p, q)
此时,q优于j,p优于q,因此p是最优解
(2) P(q, j) < P[i] < P(p, q)
此时,j优于q,p优于q,因此j,p中的一个是最优解
(3) P(q, j) < P(p, q) < P[i]
此时,j优于q,q优于p,因此j是最优解

我们会发现,在满足该种的情况下,无论如何,q都不是最优解,也就是中间的那个永远不是最优,因此,我们可以用单调队列来维护斜率上升的队列来O(1)的求解dp[i]

第三步:

​ 因为这是一个树,在遍历树的时候,遍历完子节点回溯到父节点的时候需要对队列进行还原,我们发现手动模拟的队列数组q虽然tail,head改变,但是存储的内容只有一个位置发生改变,其余改变的只有head和tail,数组中的东西依旧保存。因此我们只需要维护每次的head,tail和改变的那个值即可,详细请看代码:

/*************************************************************************
	> File Name: p.cpp
	> Author: Zcy
	> Mail: 296763002@qq.com
	> Created Time: 三  1/23 18:16:17 2019
 ************************************************************************/

#include <stdio.h>
#include <vector>
#include <ctype.h>
#define ll long long
using namespace std;

//快速读
inline int read() {
    int num=0;
    char ch=0;
    while (!isdigit(ch)) {
        ch = getchar();
    }
    while (isdigit(ch)) {
        num = (num<<3) + (num<<1) + (ch^48);
        ch = getchar();
    }
    return num;
}

//快速读
inline ll readl() {
    ll num=0;
    char ch=0;
    while (!isdigit(ch)) {
        ch = getchar();
    }
    while (isdigit(ch)) {
        num = (num<<3) + (num<<1) + (ch^48);
        ch = getchar();
    }
    return num;
}

struct node
{
	int inx;
	ll val;
	void set(int ninx, ll nval) {
		inx = ninx;
		val = nval;
	}
};
ll P[1000005] = {0}, Q[1000005] = {0}, dp[1000005] = {0}, s[1000005] = {0};
vector<node> v[1000005];
int q[1000005];

void set(int j, int i) {
	dp[i] = dp[j] + P[i] * (s[i] - s[j]) + Q[i];
}

double getp(int j, int i) {
	return 1.0 * (dp[i] - dp[j]) / (s[i] - s[j]);
}

void dfs(int inx, int tail, int head) {
	while (head + 2 <= tail && getp(q[head], q[head + 1]) <= P[inx]) head++;
	set(q[head], inx);
	while (head + 2 <= tail && getp(q[tail - 1], inx) <= getp(q[tail - 2], q[tail - 1])) tail--;
	int flag = q[tail];
	q[tail++] = inx;
	for (int i = 0; i < v[inx].size(); i++) {
		node t = v[inx][i];
		s[t.inx] = s[inx] + t.val;
		dfs(t.inx, tail, head);
	}
	q[tail - 1] = flag;
	return;
}	

int main () {
	int n, father;
	ll x;
	node t;
	n = read();
	for (int i = 2; i <= n; i++) {
		father = read();
		x = readl();
		P[i] = readl();
		Q[i] = readl();
		t.set(i, x);
		v[father].push_back(t);
	}
	q[1] = 1;
	dfs(1, 1, 1);
	for (int i = 2; i <= n; i++) {
		printf("%lld\n", dp[i]);
	}
	return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值