[BZOJ3672][UOJ#7][NOI2014]购票

试题描述

今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。

全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv

从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。

对于任意一个城市 v,我们会给出一个交通工具的距离限制 lv。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 lv  时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 pv,qv  作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 dpv+qv

每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。

输入

第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市。其中第 v 行包含 5 个非负整数 f_v,s_v,p_v,q_v,l_v,分别表示城市 v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。请注意:输入不包含编号为 1 的SZ市,第 2 行到第 n 行分别描述的是城市 2 到城市 n。

输出

输出包含 n-1 行,每行包含一个整数。其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为 1 的SZ市。

输入示例

7 3
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10

输出示例

40
150
70
149
300
150

数据规模及约定

对于所有测试数据,保证 0≤pv≤106,0≤qv≤1012,1≤fv<v;保证 0<sv≤lv≤2×1011,且任意城市到SZ市的总路程长度不超过 2×1011

输入的 t 表示数据类型,0≤t<4,其中:

当 t=0 或 2 时,对输入的所有城市 v,都有 fv=v-1,即所有城市构成一个以SZ市为终点的链;

当 t=0 或 1 时,对输入的所有城市 v,都有 lv=2×1011,即没有移动的距离限制,每个城市都能到达它的所有祖先;

当 t=3 时,数据没有特殊性质。

n=2×10^5

题解

借着此题学了学有根树分治

首先不难想出一个dp,设f(i)表示节点 i 到节点 1 所需的最小花费,有 f(i) = min{ f(j) + d(i ~ j) * p(i) + q(i) | j 为 i 祖先 & d(i ~ j) <= l(i) },其中d(i ~ j)表示节点 i 到 j 的距离,p, q, l 的意义见题目描述。

还是基本的思想,把d(i ~ j)拆开,变成dep(i) - dep(j)(dep(i)表示节点 i 到根的距离),于是上述式子转化成 f(i) - dep[i] * p(i) - q(i) = min{ f(j) + p(i) * dep(j) | 条件略 },可以用分治把它转化成一个序列问题,有一定顺序后就可以维护下凸壳了。

分治的思想是:对于子树u,找到其重心rt,分治处理u所在的以rt为根的子树,然后用u到rt这一条链上的信息更新以rt为根的其他子树上节点的信息。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;
#define LL long long
#define LD double

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
LL read() {
    LL x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
    return x * f;
}

#define maxn 200010
#define maxm 400010
#define oo 1ll << 60
int n, m, head[maxn], next[maxm], to[maxm], fa[maxn];
LL dep[maxn], f[maxn], p[maxn], q[maxn], l[maxn];

void AddEdge(int a, int b) {
	to[++m] = b; next[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; next[m] = head[a]; head[a] = m;
	return ;
}

int root, size, siz[maxn], g[maxn];
bool vis[maxn];
void getroot(int u, int pa) {
	siz[u] = 1; int maxs = 0;
	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != pa) {
		getroot(to[e], u);
		siz[u] += siz[to[e]];
		maxs = max(maxs, siz[to[e]]);
	}
	g[u] = max(maxs, size - siz[u]);
	if(g[u] < g[root]) root = u;
	return ;
}

int A[maxn], cnt;
void dfs(int u, int pa) {
	A[++cnt] = u;
	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != pa)
		dfs(to[e], u);
	return ;
}
bool cmp(int i, int j) { return dep[i] - l[i] > dep[j] - l[j]; }
LD slop(int i, int j) { return (LD)(f[i] - f[j]) / (dep[i] - dep[j]); }
int Q[maxn];
void solve(int u) {
	g[root = 0] = n+1; getroot(u, 0);
	int ni = root, rt = root; vis[root] = 1;
	if(!vis[u]) size = siz[u] - siz[rt], solve(u);
//	printf("%d %d\n", u, ni);
	cnt = 0;
	for(int e = head[ni]; e; e = next[e]) if(!vis[to[e]]) dfs(to[e], ni);
	sort(A + 1, A + cnt + 1, cmp);
	for(int i = fa[ni]; i != fa[u] && dep[i] >= dep[ni] - l[ni]; i = fa[i])
		f[ni] = min(f[ni], f[i] + (dep[ni] - dep[i]) * p[ni] + q[ni]);
	for(int i = 1, r = 0; i <= cnt; i++) {
		for(; ni != fa[u] && dep[ni] >= dep[A[i]] - l[A[i]]; ni = fa[ni]) {
			while(r > 1 && slop(Q[r-1], Q[r]) <= slop(Q[r], ni)) r--;
			Q[++r] = ni;
		}
		int L = 1, R = r + 1;
		while(R - L > 1) {
			int M = L + R >> 1;
			if(M == 1 || slop(Q[M-1], Q[M]) >= (LD)p[A[i]]) L = M;
			else R = M;
		}
		if(L < R) f[A[i]] = min(f[A[i]], f[Q[L]] + (dep[A[i]] - dep[Q[L]]) * p[A[i]] + q[A[i]]);
	}
	for(int e = head[rt]; e; e = next[e]) if(!vis[to[e]]) {
		size = siz[to[e]]; solve(to[e]);
	}
	return ;
}

int main() {
//	freopen("ex_ticket2.in", "r", stdin);
//	freopen("out.out", "w", stdout);
	n = read(); read();
	f[1] = 0;
	for(int i = 2; i <= n; i++) {
		f[i] = oo;
		fa[i] = read(); AddEdge(fa[i], i);
		dep[i] = dep[fa[i]] + read();
		p[i] = read(); q[i] = read(); l[i] = read();
	}

	size = n;
	solve(1);

	for(int i = 2; i <= n; i++) printf("%lld\n", f[i]);

	return 0;
}
时间: 2024-12-29 12:29:22

[BZOJ3672][UOJ#7][NOI2014]购票的相关文章

【BZOJ3672】[Noi2014]购票 树分治+斜率优化

[BZOJ3672][Noi2014]购票 Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城

bzoj 3672: [Noi2014]购票 树链剖分+维护凸包

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其

【bzoj3672】[Noi2014]购票 斜率优化+CDQ分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费的最小代价(中途可以经停其它点) 输入 第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到).输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市.其中第 v 行包含 5 个非负整数 $f_v,s_v,p_v,q_v,l_v$,分别表示城市 v 的父亲城市,它到

BZOJ3672: [Noi2014]购票

Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐

BZOJ3672/UOJ7 [Noi2014]购票

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用

bzoj3672: [Noi2014]购票(树形DP+斜率优化+可持久化凸包)

这题的加强版,多了一个$l_i$的限制,少了一个$p_i$的单调性,难了好多... 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ $\frac {f(j)-f(k)}{dep_j-dep_k}<p_i$ 假如没有$l_i$的限制,实际上就是上面那题... 如果多了$l_i$的限制会有什么影响呢? 类似上图的情况...红线是$l_i$的限制,如果是单调队列写法的话,P点会被删掉,但实际上P点依然有可能成为最优决策点... 这个时候一个单调队列的写法就不

[NOI2014]购票 --- 斜率优化 + 树形DP + 数据结构

题目描述 今年夏天,NOI在SZ市迎来了她30周岁的生日. 来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接. 为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1. 对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv 以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到

[BZOJ 3668&amp;UOJ #2][Noi2014]起床困难综合症(贪心)

Description 21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳.作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争.通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间.正是由于 drd 的活动,起床困难综合症愈演愈烈,以惊人的速度在世界上传播.为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙.历经千辛万苦,atm 终于来到了 drd 所在的

BZOJ 3672 NOI2014 购票

这题好神啊..好神啊...好神啊... 首先列出N2的DP方程较易. 从DP方程很容易看出来是斜率优化. 如何进一步优化? 考虑对当前点以上的链建立一个下凸包. 维护凸包就可以,但不是很好写. 观察到方程可以必然由它的祖先节点转移.很像Cash那道题. 尝试CDQ分治,每次先递归处理根所在的子树. 然后用根所在的子树,对当前点更新答案,对其他点进行根据dis-lim排序,维护栈即可. 考虑到复杂度,我们需要对树进行点分治. code: #include<iostream> #include&l