这是NOIday2压轴题了,粗看之下不就就是树形dp吗?
s[x]=min{s[t]+p[x](l[x]−l[t])}+q[x],(t∈x.fathers)
一看n的大小:200000,哎啊妈呀O(n2)TLE,得赶紧找优化:
设k为
s[k]+p[x](l[x]−l[k])<s[j]+p[x](l[x]−l[j])
也即:
s[k]−s[j]<p[x](l[k]−l[j])⇔s[k]−s[j]l[k]−l[j]<p[x]
我们看到了什么?对,斜率优化.
这样,我们在计算s[x]时就需要维护一个凸包了.凸包上的点以l[x]为横坐标,以s[x]为纵坐标,当然对根也即1结点,它就是原点
这是一个凸包(省略了坐标轴)
到这里,我们再回顾下题目,什么!?还有距离限制!那么等于说我们不能只简单的维护一个凸包了,这对我们来说是个打击.不过没关系,我们加上距离限制,那么我们只能使用凸包的一段…不仔细想一下,当我们截去凸包的一段时,有可能加入新的可行点!
我们从红色箭头所指的地方截断凸包,但我们发现,原来插入结点时所删去的两个结点是有可能作为答案的!例如:
在这里,我们发现新加入的第二个点就是答案因此,我们所面对的问题在加入了距离限制后变得棘手的多了,当然,这也是此题的魅力所在.
我们来考虑这个问题怎么处理:
凸包是可合并的,即我们从两个凸包中分别获得了答案但最后,我们选择了上面的结点
嘿!我们就可以使用线段树了!我么要开一个O(nlogn)大小的线段树,线段树的结点保存对应区间的凸包(准确的说是点序列).考虑到凸包的可合并性,我相信你很快能想到如何在该线段树中寻找答案,用时应该是O(log2n)的.
我们考虑如何在线段树中插入一个结点,这并不难,我们自上到下向线段树中插入结点,但是!这是一棵树!树是有分岔的!当我们算完一条树链时,我们要返回到分岔点,在这之中,必应有结点的重新插入!如图:
我们不可能真的将它们重新插入,那么我们在插入时就要小心,保存好插入新的结点时在线段树中覆盖的原位置数据和原凸包长度,在我们删除结点时应该将它们恢复,这样,我们每一次插入用时O(log2n),删除用时O(logn),这对n<100000已经足够了.(本人比较菜,神犇请勿吐槽)
我们回头来看我们的线段树,其实这就是可持久化线段树的一种,因为我们还要不断用到以前的版本(恢复结点).当然主席树(函数式线段树)也是可持久化线段树的一种,它由fotile主席发明,可以做到O(logn)时间内求给定区间第k大.