【算法进阶详解】线段树应用

前言

本文不讲解线段树的基础理论,而是讲解线段树的应用,如线段树优化 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=1maxi1{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 1ajai1 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 类型:

  1. 对状态有增加同时有关 i i i j j j 的权值。
  2. 对状态的转移有限制。

什么意思呢?我们一个一个看。

类型 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,j1+lastk+1,ifirstk+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,i1] 加上 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,p1] 先减去 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,i1] 加上 i i i
查询时直接查询 [ 1 , i − 1 ] [1, i - 1] [1,i1] 的最大值即可。

区间加区间最大值可以用线段树维护,时间复杂度 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+(ki)+(nai)

找到 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 之间没有过多的联系,只是分开的两步。

练习题

[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] 数学计算: 逆元用不了就使用线段树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值