李超树学习笔记
李超树学习笔记
李超树是一种维护区间最优线段/直线的线段树。
称一条线段能成为区间
- 该线段的定义域完整覆盖了区间 ;
- 该线段在区间中点处最优,并且这条线段不是
[l,r] 的祖先区间的最优线段。
核心思想:标记永久化!只需满足包括单点的所有线段树区间“最优线段”中含有该单点的最优线段即可,依此保证复杂度
考虑插入一条线段时如何更新如何更新:(可以画个图理解一下)
如果新线段斜率大于原线段,
-
如果新线段在
mid 处更优,则新线段在右半区间一定 最优,旧线段在左半区间可能最优; -
反之,旧线段在左半区间一定最优,新线段在右半区间可能最优。
如果新线段斜率小于原线段,
- 如果新线段在
mid 处更优,则新线段在左半区间一定最优,旧线段在右半区间 可能 最优; - 反之,旧线段在右半区间一定最优,新线段在左半区间 可能 最优。
另一种更简洁的实现:将新直线
- 若在左端点处
f 更优,则f,g 必然在左半区间产生交点,f 只有在左半区间才可能优于g ,向左递归下传f - 若在右端点处
f 更优,则f,g 必然在右半区间产生交点,f 只有在右半区间才可能优于g ,向右递归下传f - 左右端点
f 均不如g ,f 不可能成为答案,不用下放
模板题
P4097 [HEOI2013]Segment
本题要求维护区间最优线段,我们按上面所说的模拟即可。考虑复杂度,我们将线段分割到
本题易挂点:注意特判
P4254 [JSOI2008]Blue Mary开公司
本题中插入的是直线 ,所以不需要将线段分割到其对应的区间,直接更新即可。复杂度
李超树维护一次函数
其实就是维护直线/线段。但因为这道题不算很板,所以单独开个分类。
P4069 [SDOI2016]游戏
进行第一个操作时,我们可以把路径拆为两部分
- 对于
(s,lca) 上的点x ,加上的数字为a\times(dis_s-dis_x)+b ,设K=-a,B=a\times dis_s+b,X=dis_x ,那么对(s,lca) 上的每一个点添加一个数,相当于对区间(s,lca) 插入了一条y=KX+B 的线段。 - 对于
(lca,t) 上的点x ,加上的数字为a\times(dis_s+dis_x-2dis_{lca})+b ,设K=a,B=a\times(dis_s-2dis_{lca})+b,X=dis_x ,那么对(s,lca) 上的每一个点添加一个数,相当于对区间(s,lca) 插入了一条y=KX+B 的线段。
操作二:维护区间最小值,包含在询问区间内部的节点直接返回最小值,路径上经过的节点取其最优线段在本区间内的最小值(端点处)作为标记,和左、右儿子中的答案去
还有一些实现上的细节见代码注释。
李超树维护斜率优化
这部分很大程度上参考了 @panyf 巨佬的博客,在这里orz!
若 dp 式子可化为
对于
P4655 [CEOI2017]Building Bridges
设
稍作化简得:
设
问题转化为,插入直线
代码细节:一定要给
P4027 [NOI2007]货币兑换
首先要注意到一个关键性质:必然存在一种最优的买卖方案满足每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。(其实这个性质就写在题面最下方)
设
可得:
解方程得
若第
但是
CF932F Escape Through Leaf
一个显然的 dp 式子:
P2305 [NOI2014]购票
先丢个朴素的 dp 式子
稍微推一下得
如果
但更好的做法是利用出栈序,也就是每个结点离开 dfs 的顺序 。(再次sto @panyf 巨佬 orz!)下面引用一段他的博客。
直接在点
i 及其最远的满足d_i-d_j\leq l_i 的祖先j 的出栈序对应的区间上查询。容易发现区间内不在i 和j 的路径上的点都未被访问到,不会对答案产生贡献。这样就不需要树链剖分了。时间复杂度O(n\log^2n) n),空间复杂度O(n\log n) 。
但因为树剖的常数很小,李超树常数又很大,所以树剖的
再扔几个例题
- CF631E Product Sum
- P5785 [SDOI2012]任务安排
- 题解CF1175G Yet Another Partiton Problem。
其实基本上所有的斜率优化题都可以用李超树做,但是比单调队列多个 log(和很大的常数)。于是我现在看到斜率优化题就想用李超树,真的比单调队列好想好写多了!