[Luogu P2305] [BZOJ 3672] [NOI2014]购票

洛谷传送门
BZOJ传送门

题目描述

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

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

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

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

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

输入输出格式

输入格式:

1 1 1 行包含 2 2 2个非负整数 n , t n,t n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。

输入文件的第 2 2 2 n n n 行,每行描述一个除SZ之外的城市。其中第 v v v 行包含 5 5 5 个非负整数 f v , s v , p v , q v , l v f_v,s_v,p_v,q_v,l_v fv,sv,pv,qv,lv,分别表示城市 v v v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。

请注意:输入不包含编号为 1 1 1 的SZ市,第 2 2 2 行到第 n n n 行分别描述的是城市 2 2 2 到城市 n n n

输出格式:

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

其中第 v v v 行表示从城市 v + 1 v+1 v+1 出发,到达SZ市最少的购票费用。

同样请注意:输出不包含编号为 1 1 1 的SZ市。

输入输出样例

输入样例#1:
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 
输出样例#1:
40 
150 
70 
149 
300 
150

说明

从每个城市出发到达 SZ的路线如下(其中箭头表示一次直达):

城市 2 2 2:只能选择 2 → 1 2 → 1 21,花费为 2 × 20 + 0 = 40 2 × 20 + 0 = 40 2×20+0=40

城市 3 3 3:只能选择 3 → 1 3 → 1 31,花费为 5 × 10 + 100 = 150 5 × 10 + 100 = 150 5×10+100=150

城市 4 4 4 : 由 于 4 + 2 = 6 ≤ l 4 = 10 4 + 2 =6 ≤ l_4 = 10 4+2=6l4=10,故可以选择 4 → 1 4 →1 41。若选择 4 → 1 4 → 1 41,花费为 ( 4 + 2 ) × 10 + 10 = 70 (4 +2) × 10 + 10 = 70 (4+2)×10+10=70 ; 若选 择 4 → 2 → 1 4 → 2 → 1 421,则花费为 ( 4 × 10 + 10 ) + ( 2 × 20 + 0 ) = 90 (4 ×10 + 10) + (2 × 20 + 0) =90 (4×10+10)+(2×20+0)=90;因此选择 4 → 1 4 → 1 41

城市 5 5 5:只能选择 5 → 2 → 1 5 →2 → 1 521 , 花费为 ( 9 × 1 + 100 ) + ( 2 × 20 + 0 ) = 149 (9 × 1 +100) + (2 × 20 + 0) = 149 (9×1+100)+(2×20+0)=149;无法选择 5 → 1 5 → 1 51,因为 l 5 = 10 l_5 =10 l5=10,而城市 5 5 5 到城市 1 1 1 总路程为 9 + 2 = 11 > 5 9 + 2 = 11 > 5 9+2=11>5,城市 5 5 5 不能直达城市 1 1 1

城市 6 6 6:若选择 6 → 1 6 → 1 61,花费为 ( 5 + 5 ) × 20 + 100 = 300 (5 + 5) × 20 + 100 = 300 (5+5)×20+100=300;若选择 6 → 3 → 1 6 → 3 →1 631,花费为 ( 5 × 20 + 100 ) + ( 5 × 10 + 100 ) = 350 (5 × 20 + 100) + (5 × 10 + 100) = 350 (5×20+100)+(5×10+100)=350;因此选择 6 → 1 6 → 1 61

城市 7 7 7:选择 7 → 4 → 1 7 → 4 → 1 741,花费为 ( 4 × 20 + 0 ) + ( ( 4 + 2 ) × 10 + 10 ) = 150 (4 × 20 + 0) + ((4 + 2) × 10 + 10) = 150 (4×20+0)+((4+2)×10+10)=150

其他方案均比该方案差。

img

数据规模

img

解题分析

d i s [ i ] dis[i] dis[i] i i i号节点到 1 1 1号节点的距离, 那么有:
a n s [ i ] = m i n ( a n s [ j ] + ( d i s [ i ] − d i s [ j ] ) ∗ p [ i ] ) + q [ i ] ans[i]=min(ans[j]+(dis[i]-dis[j])*p[i])+q[i] ans[i]=min(ans[j]+(dis[i]dis[j])p[i])+q[i]
如果 d i s [ k ] > d i s [ j ] dis[k]>dis[j] dis[k]>dis[j], 且 k k k j j j更优, 就有:
a n s [ k ] + ( d i s [ i ] − d i s [ k ] ) ∗ p [ i ] &lt; a n s [ j ] + ( d i s [ i ] − d i s [ j ] ) ∗ p [ i ] ( d i s [ k ] − d i s [ j ] ) ∗ p [ i ] &gt; a n s [ k ] − a n s [ j ] p [ i ] &gt; a n s [ k ] − a n s [ j ] d i s [ k ] − d i s [ j ] ans[k]+(dis[i]-dis[k])*p[i]&lt;ans[j]+(dis[i]-dis[j])*p[i] \\ (dis[k]-dis[j])*p[i]&gt;ans[k]-ans[j] \\ p[i]&gt;\frac{ans[k]-ans[j]}{dis[k]-dis[j]} ans[k]+(dis[i]dis[k])p[i]<ans[j]+(dis[i]dis[j])p[i](dis[k]dis[j])p[i]>ans[k]ans[j]p[i]>dis[k]dis[j]ans[k]ans[j]
那么显然可以斜率优化。

但是这玩意是在树上的, 而且 p [ i ] p[i] p[i]并不单调, 还有一个 l i l_i li的限制。

p [ i ] p[i] p[i]不单调的问题我们在凸包上二分就好了, 而树上的问题考虑 D F S DFS DFS回溯时撤销即可。 这个限制实际上是要我们查询一段连续区间内的点构成凸包上的最优决策点, 直接用线段树维护每个区间内的点构成的凸包。

查询时查询线段树上 l o g ( N ) log(N) log(N)个节点, 合并每个凸包上的最优决策点即可。

考虑如何资瓷撤销操作: 实质上我们插入一个点是弹出了队列尾端的若干点, 直接记录插入位置原来的点的值和原来的队列长度即可。

总复杂度 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 210500
#define db double
#define ls (now << 1)
#define rs (now << 1 | 1)

template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, arr = 1, cnt, typ;
struct Node {int bg, len;} tree[MX << 2];
struct Edge {int to, nex; ll len;} edge[MX << 1];
int v[MX * 25], head[MX];
ll dp[MX], dis[MX], mul[MX], base[MX], lim[MX], Dep[MX];
int tagl[MX][25], tagv[MX][25];

IN void add(R int from, R int to, R ll len) {edge[++cnt] = {to, head[from], len}, head[from] = cnt;}
IN db slope(R int pre, R int cur) {return 1.0 * (dp[cur] - dp[pre]) / (dis[cur] - dis[pre]);}
void build(R int now, R int lef, R int rig)//preserve space for convex
{
	tree[now].bg = arr; arr += rig - lef + 1;
	if (lef == rig) return;
	int mid = lef + rig >> 1;
	build(ls, lef, mid), build(rs, mid + 1, rig);
}
IN int findpos(R int rb, R ll lmt)//Get the highest pos available
{
	int lef = 1, rig = rb, ans, mid;
	W (lef <= rig)
	{
		mid = lef + rig >> 1;
		if (Dep[mid] < lmt) lef = mid + 1;
		else ans = mid, rig = mid - 1;
	}
	return ans;
}
IN int findrgt(R int lb, R int rb, R int pos)//get the rightmost dot on the convex when insert pos
{
	if (lb >= rb) return rb;
	int lef = lb + 1, rig = rb, mid;
	W (lef < rig)
	{
		mid = lef + rig + 1 >> 1;
		if (slope(v[mid - 1], v[mid]) > slope(v[mid], pos)) rig = mid - 1;
		else lef = mid;
	}
	if (slope(v[lef - 1], v[lef]) > slope(v[lef], pos)) --lef;
	return lef;
}
IN int findans(R int lb, R int rb, R ll lmt)//get the best choice on the convex
{
	if (lb >= rb) return rb;
	int lef = lb + 1, rig = rb, mid;
	W (lef < rig)
	{
		mid = lef + rig >> 1;
		if (lmt > slope(v[mid - 1], v[mid])) lef = mid + 1;
		else rig = mid;
	}
	if (lmt <= slope(v[lef - 1], v[lef])) --lef;
	return lef;
}
int query(R int now, R int lef, R int rig, R int lb, R ll Mul)
{
	if (!tree[now].len || rig < lb) return -1;
	if (lb <= lef) return findans(tree[now].bg, tree[now].len + tree[now].bg - 1, Mul);
	int mid = lef + rig >> 1, lans, rans;
	lans = query(ls, lef, mid, lb, Mul);
	rans = query(rs, mid + 1, rig, lb, Mul);
	if ((~lans) && (~rans))
	{
		if (Mul > slope(v[lans], v[rans])) return rans;
		return lans;
	}
	return (~lans) ? lans : rans;
}
void modify(R int now, R int lef, R int rig, R int id, R int tar, R int dep)
{
	int pos = findrgt(tree[now].bg, tree[now].bg + tree[now].len - 1, tar);
	tagv[tar][dep] = v[++pos]; tagl[tar][dep] = tree[now].len;//record the pos && value before modification
	v[pos] = tar; tree[now].len = pos - tree[now].bg + 1;
	if (lef == rig) return;
	int mid = lef + rig >> 1;
	if (id <= mid) modify(ls, lef, mid, id, tar, dep + 1);
	else modify(rs, mid + 1, rig, id, tar, dep + 1);
}
void reset(R int now, R int lef, R int rig, R int id, R int tar, R int dep)
{
	v[tree[now].bg + tree[now].len - 1] = tagv[tar][dep];
	tree[now].len = tagl[tar][dep];
	if (lef == rig) return;
	int mid = lef + rig >> 1;
	if (id <= mid) reset(ls, lef, mid, id, tar, dep + 1);
	else reset(rs, mid + 1, rig, id, tar, dep + 1);
}
void solve(R int now, R int dep)
{
	if (now ^ 1)
	{
		ll lmt = dis[now] - lim[now];
		int pos = v[query(1, 1, n, findpos(dep - 1, lmt), mul[now])];
		dp[now] = base[now] + 1ll * (dis[now] - dis[pos]) * mul[now] + dp[pos];
	}
	modify(1, 1, n, dep, now, 1);
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		dis[edge[i].to] = dis[now] + edge[i].len;
		Dep[dep + 1] = dis[edge[i].to];
		solve(edge[i].to, dep + 1);
	}
	reset(1, 1, n, dep, now, 1);
}
int main(void) 
{
	int foo;
	ll bar;
	in(n), in(typ);
	for (R int i = 2; i <= n; ++i)
	{
		in(foo), in(bar), in(mul[i]), in(base[i]), in(lim[i]);
		add(foo, i, bar);
	}
	build(1, 1, n); solve(1, 1);
	for (R int i = 2; i <= n; ++i) printf("%lld\n", dp[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值