互测赛(4)总结
0.前言
其实是 KTT 学习笔记。
1.正文
KTT,全称 K什么 T什么 T什么,主要用来解决区间加正数,求区间最大子段和问题。
如果没有修改操作,这个东西是好做的。我们直接使用线段树,每个节点维护当前节点的区间和,区间最大子段和,最大前缀和和最大后缀和。设其分别为 \(sum,val,lmax,rmax\)。pushup 是简单的:
- \(p.sum=ls.sum+rs.sum\)。
- \(p.lmax=\max(ls.lmax,ls.sum+rs.lmax)\)。
- \(p.rmax=\max(rs.rmax,rs.sum+ls.rmax)\)。
- \(p.val=\max(ls.val,rs.val,ls.rmax+rs.lmax)\)。
这样就可以做到 \(O(n+q\log n)\) 的时间复杂度。
如果有区间加正数的操作,这个问题就变得相当困难了。
(Mortis尖叫.jpg)
假设,现在给某个区间加了 \(d\),并且取 \(\max\) 时决策不变,那么 \(val\) 就会变成 \(val+d\times Len\)。其中 \(Len\) 是最大子段和对应的区间的长度。
不过这样的情况并不会经常出现。但是这是有启发性的。我们可以把 \(val+d\times Len\) 看作一个和 \(d\) 相关的一次函数。那么每次 pushup 时就是一次函数取 \(\max\)(这里是根据 \(d=0\) 时的取值来取 \(\max\),原因待会就能理解)。
如何解决决策变化的问题?通过计算一次函数的交点,我们可以得到一个阈值 \(x\),它表示当前节点在 \(d\geq x\) 时,\(lmax,rmax,val\) 中就会有至少一个一次函数的取值(就是 pushup 时 \(\max\) 取的是哪个一次函数)变化。如果现在加入的 \(d < x\),那么就可以直接将所有一次函数的 \(b\) 值更新,这相当于我们对一次函数进行了向左平移。也就是说,\(x\) 值也要减去 \(d\)。
否则,直接往两边递归,直到发现某一个子树满足其 \(d < x\)。这就相当于一次函数平移后,我们的一次函数交点已经移到 \(0\) 之前了。那我们钦定按照位置 \(0\) 的值取 \(\max\) 就不行了,需要重新计算。你会发现,这里的一次函数在正常时取值就应该是 \(0\) 位置的取值(即答案),将其变成一次函数只是为了方便我们进行修改操作。更新完子树后再 pushup 就行了。
因为当前一次函数的决策和子树的一次函数有关,所以当子树的一次函数变化时,当前节点的一次函数也可能变化。也就是说,\(x\) 也需要 pushup,并且在 pushup 时需要和 \(ls.x\) 与 \(rs.x\) 取 \(\min\)。除此之外,还需要计算当前节点三个决策中各个一次函数的交点,然后再取 \(\min\)。然后维护一下一次函数就做完了。
看着很暴力,但是时间复杂度其实是三个 \(\log\) 的。不会分析。
写的啥呀。

浙公网安备 33010602011771号