【斜率优化的新理解】 Harbingers

CEOI 2009 Harbingers

【题目大意】

给出一棵n个节点树,根为1,给出树上每条边的长度di和这条边连接的两个节点ui,vi。当一个点i需要向根节点传递信息时,需要先花费Si的时间把信息告知节点i的信使,然后信使开始往根节点的方向移动。当信使到的一个新的节点j时,他有两个选择:1,自己继续向根节点前进。2,花Sj的时间把信息交给当地的信使,然后由当地的信使向根节点传递信息。

i个节点的信使通过一个单位长度的路程所花费的时间Vi3<=n<=100000,其它出现的数子<=10^9。问从每个节点传递信息到根节点的最短时间。

【题解】

这题动态规划的解法的是显然的。令f[i]为第i个节点传递信息到根节点信息的最短时间。对于i>1,不难列出方程f[i]=min(f[j]+V[i]*(D[i]-D[j])+S[i])ji的祖先。

首先考虑条链上的问题。将转移方程拆开,得到f[i]=min(f[j]-V[i]*D[j]))+V[i]*D[i]+S[i]

因为v[i]*D[i]+S[i]只与i有关,所以它不影响决策,先忽略。

我们来看f[i] = min(f[j]-V[i]*D[j]).

假设我们有一个函数g(x).

我们要使得f[i]最小,不妨设f[i]为b(截距),f[j]为y(函数值),V[i]为k(函数g(x)的斜率),D[j]为x(自变量),则有b = g(x) - k*x

移项得g(x) = k*x + b;

我们要使b尽量小,就是要维护一个(x,g(x))的下凸壳(即(D[j],f[j])为点的下凸壳),这样子当我们在做f[i]时,得到一个已知量V[i](即g(x)斜率k),把斜率为k的直线从x轴负方向无穷远处向上移碰到的第一个点就是最优的。因此不在下凸壳上的点没有意义。


又因为在转移的过程中,当前节点iD[i]值单调递增,所以可以用一个栈按照从栈底到栈顶斜率严格递增的顺序,来维护下凸壳,每次加入一个点(D[i],f[i])时,首先删除栈顶f[j]>=f[i]的元素,然后删除加入i后导致斜率不递增的栈顶元素,然后将i加入栈中。对于每次查询,可以通过二分查找找到满足和下一个节点的斜率不小于当前节点V[i]的最靠近栈顶的节点,即为最优值。

对于一条链的问题,每个节点最多进栈一次,出栈一次,因此这两种操作可以暴力实现,总的时间复杂度为O(n),每次查询为O(logn),最多n次查询,因此总的时间复杂度为O(nlogn)

对于一颗树上的问题,同样可以用一条链上的算法。但从子节点返回父亲节点时,需要把被删除的元素重新入栈,这样每个点可能进出栈多次,用暴力实现会TLE。但不难发现,每个节点入栈删除的元素只会是栈顶开始连续的若干个元素,然后入栈。因此如果用一个数组存储单调栈的话,可以用一个元素记录父节点原先栈的大小,令一个元素记录这个节点入栈的位置上原来的元素。这样就可以用O(1)的时间将栈复原。然后对于维护下凸壳的操作,可以用二分查找在O(logn)的时间内计算出要删除的元素棵树。这样维护、查询的复杂度均为O(logn),总的时间复杂度依然为O(nlogn),可以通过本题。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值