投资公司网站模板/网络营销名词解释答案
【算法进阶详解】线段树应用
- 前言
- 线段树优化 DP
- 类型 1
- 类型 2
- 一些特殊的线段树优化 DP 类型
- 类型 3
- 类型 4
- 练习题
- 扫描线
- 例题讲解
- 练习题
- 一些奇妙 trick(不详细讲解)
前言
本文不讲解线段树的基础理论,而是讲解线段树的应用,如线段树优化 DP,扫描线。如有问题欢迎指出。
线段树优化 DP
有的时候,DP 的转移方程的复杂度很高,线段树优化可以将其优化。
以最长上升子序列为例,转移式为:
f i = max j = 1 i − 1 { f j } ( a j < a i ) + 1 f_i = \max_{j = 1}^{i - 1} \{f_j\}(a_j < a_i) + 1 fi=j=1maxi−1{fj}(aj<ai)+1
直接朴素做是 O ( n 2 ) O(n^2) O(n2) 的,我们考虑如何优化。
我们发现,转移相当于在之前求的所有 f f f 中找到 1 ≤ a j ≤ a i − 1 1 \le a_j \le a_i - 1 1≤aj≤ai−1 的 f j f_j fj 的最大值。于是就相当于我们在求完 f i f_i fi 后就把 f i f_i fi 插到 a i a_i ai 位置。计算状态时查询 1 1 1 ~ a i a_i ai 的最小值。发现可以使用线段树维护区间 m i n min min, O ( log ( n ) ) O(\log(n)) O(log(n)) 插入和删除,整个 DP 优化至 O ( n log ( n ) ) O(n\log(n)) O(nlog(n))。
这是一个例子。有两种种较为经典的线段树优化 DP 类型:
- 对状态有增加同时有关 i i i 和 j j j 的权值。
- 对状态的转移有限制。
什么意思呢?我们一个一个看。
类型 1
CF1527E - Partition Game。
很容易写出状态转移方程:
f i , j = m i n k < i f k , j − 1 + l a s t k + 1 , i − f i r s t k + 1 , i f_{i, j} = min_{k < i} {f_{k, j - 1} + last_{k + 1, i} - first_{k + 1, i}} fi,j=mink<ifk,j−1+lastk+1,i−firstk+1,i
靠虑如何优化。
发现转移没有限制,但是转移中 l a s t last last 和 f i r s t first first 无法直接拆开,可以用线段树维护每一个位置当前 i i i 下每个 j j j 的值。
考虑每加入一个数, l a s t last last 和 f i r s t first first 有什么变化。
先看 f i r s t first first。找出上一个 a p = a i a_p = a_i ap=ai 的下标 p p p。对于所有 ≤ p \le p ≤p 的下标 j j j,已经有了第一次出现的位置,无需更改;对于所有 > p > p >p 的下标 k k k,第一次出现的位置就是 i i i,因此将 [ p + 1 , i − 1 ] [p + 1, i - 1] [p+1,i−1] 加上 i i i。
再看 l a s t last last。同样找出上一个 a p = a i a_p = a_i ap=ai 的下标 p p p。对于所有 ≤ p \le p ≤p 的下标 j j j,已经记录的 p p p,而应为 i i i,所以将 [ 1 , p − 1 ] [1, p - 1] [1,p−1] 先减去 p p p 再加上 i i i;对于所有 > p > p >p 的下标 k k k,最后一次出现的位置就是 i i i,将 [ p + 1 , i − 1 ] [p + 1, i - 1] [p+1,i−1] 加上 i i i。
查询时直接查询 [ 1 , i − 1 ] [1, i - 1] [1,i−1] 的最大值即可。
区间加区间最大值可以用线段树维护,时间复杂度 O ( k n log ( n ) ) O(kn\log(n)) O(knlog(n))。
对于第 1 种情况,用线段树直接维护转移值即可。
类型 2
最长上升子序列就是一个很好的例子。
将权值作为下标,DP 数组作为权值插入线段树,查询一个区间的值。
一些特殊的线段树优化 DP 类型
有一些 DP 很神奇。
类型 3
CF675E - Trains and Statistic。
设 f i f_i fi 为所有点到 i i i 的最小费用之和。发现每次走到当前路线终点,再找到能走的 a a a 最大的路线继续走,设新走的路线编号为 k k k 则:
f i = f k + ( k − i ) + ( n − a i ) f_i = f_k + (k - i) + (n - a_i) fi=fk+(k−i)+(n−ai)
找到 k k k 相当于在 [ i + 1 , a i ] [i + 1, a_i] [i+1,ai] 中找最大的 k k k,线段树即可,时间复杂度 O ( n log ( n ) ) O(n\log(n)) O(nlog(n))。
本题需要线段树优化 DP,但是线段树和 DP 之间没有过多的联系,只是分开的两步。
类型 4
[NOI1998] 免费的馅饼。
首先按照 t t t 排序,得到转移方程:
f i = max ( max p i < p j , 2 t i − 2 t j ≥ p j − p i f j , max p i > p j , 2 t i − 2 t j ≥ p i − p j f j ) + a i f_i = \max(\max_{p_i < p_j, 2t_i - 2t_j \ge p_j - p_i} f_j, \max_{p_i > p_j, 2t_i - 2t_j \ge p_i - p_j} f_j) + a_i fi=max(pi<pj,2ti−2tj≥pj−pimaxfj,pi>pj,2ti−2tj≥pi−pjmaxfj)+ai
由于两部分相似,所以我们先只考虑第一部分,第二部分类似:
f i = max p i < p j , 2 t i − 2 t j ≥ p j − p i f j + a i f_i = \max_{p_i < p_j, 2t_i - 2t_j \ge p_j - p_i} {f_j} + a_i fi=pi<pj,2ti−2tj≥pj−pimaxfj+ai
f i = max p i < p j , 2 t i + p i ≥ 2 t j + p j f j + a i f_i = \max_{p_i < p_j, 2t_i + p_i \ge 2t_j + p_j} {f_j} + a_i fi=pi<pj,2ti+pi≥2tj+pjmaxfj+ai
然后发现有两个限制,线段树只能满足一个限制,又观察发现算上排序处理的限制一共三个限制只有两个变量,因此考虑是否有多余的限制。先把限制列出来:
- t i > t j t_i > t_j ti>tj
- p i < p j p_i < p_j pi<pj
- 2 t i + p i ≥ 2 t j + p j 2t_i + p_i \ge 2t_j + p_j 2ti+pi≥2tj+pj
发现 1 和 2 不能推出 3。
然后就不会了。
再进行观察,我们惊奇的发现:2 和 3 能推出 1!
于是按照 2 和 3 中的任意一个排序,另一个线段树搞即可。
练习题
[USACO12OPEN] Bookshelf G
CF1304F2 - Animal Observation (hard version)
扫描线
一个线段树应用的大点,你可以把它理解为从前往后扫,具体扫什么后面会讲。
例题讲解
[SDOI2009] HH的项链
经典扫描线题。
i i i 从 1 1 1 到 n n n 扫。一个神仙操作是,开一个线段树,每个位置表示当前位当前颜色是不是到 i i i 时的最后一个当前颜色。将查询按右端点排序,查询时先让 i i i 扫到右端点,然后查询区间和就可以了。
为什么,因为每个位置都是最后一个位置,如果某个颜色算进去,那么由于记录的是最后一个,区间内只有一次,所以不会算重;如果不算进去说明最后一个次颜色在区间前,正常也没有这个颜色。
练习题
P4113 [HEOI2012]采花
P8868 [NOIP2022] 比赛
一些奇妙 trick(不详细讲解)
P5459 [BJOI2016] 回转寿司: 区间和转前缀和。
[ABC342G] Retroactive Range Chmax: 每个区间维护一个 set。
P4588 [TJOI2018] 数学计算: 逆元用不了就使用线段树。