吉司机线段树学习笔记

吉司机线段树

处理 区间最值操作于历史最值问题的数据结构

参考著名ppt Segment Tree BeatsSegment\ Tree\ Beats 与jry2016年论文

大概很久之前就写了大部分,最近又会了点,但离全部学会还有一定距离

区间最值操作

Problem 1

维护一个序列,支持区间取 min\min ,区间求和,求 max\max

做法

线段树上维护区间和 sumsum,最大值 mxmx,次大值 scsc 与最大值出现次数 cntcnt

区间对 xxmin\min 操作暴力递归区间

  1. mxxmx\leq x,结束递归
  2. sex<mxse\leq x<mx,将区间最大值修改为 xx,更改区间和,打上区间取 min\min 标记
  3. x<sex<se,暴力递归左右儿子

时间复杂度 O(nlogn)O(nlogn)

时间复杂度分析

设势能函数 Φ(T)\Phi(T) 表示线段树中每个节点维护的区间最大值是否与父亲的最大值相同

将每一个节点的贡献记在其对应的叶子节点上,考虑一个叶子最多只存在一次贡献

Φ(T)n\Phi(T)\leq n

考虑每一次暴力区间取 min\min 对势能函数的影响

若该次操作为对 vvmin\min,且在节点 xx 处停止,则 sexv<sefaxse_x\leq v<se_{fa_x}

  1. mxx<mxfaxmx_x<mx_{fa_x},则 mxx=sefaxmx_x=se_{fa_x},那么 mxfaxv,mxxvmx_{fa_x}\to v,mx_x\to vΦ(T)\Phi(T) 将减少1
  2. mxx=mxfaxmx_x=mx_{fa_x},对于此种情况 mxfaxv,mxxvmx_{fa_x}\to v,mx_x\to vΦ(T)\Phi(T) 无影响

考虑修改过程中的每一个1类点到根的路径最多存在 lognlogn 个2类点

那么时间复杂度 O(nlogn)O(nlogn)

Problem 2

维护一个序列,支持区间取 min\min ,区间加,区间求和,求 max\max

做法

沿用 Problem 1中的做法,多维护一个懒标记处理区间加

时间复杂度分析

考虑沿用 Problem 1中的证明,考虑一次区间加最多使 Φ(T)\Phi(T) 增加 lognlogn

可得时间复杂度 O(nlog2n)O(nlog^2n)

吉老师在其论文中提到这个做法的实际表现近似于 O(nlogn)O(nlogn),目前未有方法证明该结论

Problem 3

维护一个序列,支持区间取 min,max\min,\max,区间求和

做法

类似 min\min 的维护,max\max 同理

时间复杂度分析

O(nlog2n)O(nlog^2n)

Problem 4

维护一个序列,支持区间取 and,or\text{and},\text{or},区间求 max\max

做法

考虑如何优化暴力

对于 and v\text{and}\ v 操作

  1. 若区间内 or\text{or}sosovv 的子集,可直接返回

  2. 若区间内 and\text{and}sasavv 的交集与 ororsosovv 的交集相同

    则均为1的bit将全部保留,仅部分为1的bit将去除,最大值直接与 vv 取交即可,并打上懒标记

对于 or v\text{or}\ v 操作类似处理

时间复杂度分析

对线段树上的每个节点 uu 设一个势能函数 Φ(u)=Φ(lsu)+Φ(rsu)+i=0w[ibit]\Phi(u)=\Phi(ls_u)+\Phi(rs_u)+\sum_{i=0}^w[第i个bit在区间内不全相同]

显然 Φ(root)=O(nw)\Phi(root)=O(nw)

考虑每一次到达被询问区间包含的点 uu 后继续递归,代表这个区间内仍存在至少一个 bitbit 不全相同

and v\text{and}\ v 操作等价于 vv 为0的bit全变0,or v\text{or}\ v 操作等价于 vv 为1的bit全变1)

那么每遍历一个节点 Φ(root)\Phi(root) 将减少1

考虑势能将在仅有部分区间被操作点处增加

众所周知,线段树上定位一个区间需遍历 O(logn)O(logn)​ 个点,这些点可能会存在原本全相同的bit被改不相同的情况

每个操作最多给 Φ(root)\Phi(root) 带来 O(wlogn)O(wlogn) 的增长

则最终复杂度为 O(qlogn+nw+qwlogn)O(qlogn+nw+qwlogn)

历史版本问题

大致分为 历史最大值历史最小值历史版本和

均为字面意思

Problem 1

对于一个序列,支持区间加,区间覆盖,查询区间最大值,查询区间历史最大值

CPU监控

做法

我们先忽略区间覆盖,完成这个问题

对于一次区间加操作,传统的做法是在线段树上维护一个懒标记

我们考虑对懒标记的一种新理解

实际上,我们的懒标记是每个线段树节点上维护的一个操作序列

而进行区间加操作时,我们对懒标记的修改相当于给操作序列后面添加一个数

由于下传标记的及时性,我们每次下传懒标记相当于把整个操作序列加入到儿子操作序列的末尾

那么因为我们只想知道区间和,我们仅需维护操作序列的总和即可,也就是 AdduAdd_u

我们考虑怎样求历史最大值

对于一个线段树上的区间,实际上操作序列对历史最大值的贡献即区间最大值加上操作序列最大的前缀和

我们定义 PreuPre_u 表示节点 uu​ 的操作序列的最大前缀和,维护 hmxuhmx_u 表示节点 uu 所代表区间的历史最大值

同样维护操作序列总和 AdduAdd_u

考虑下传操作序列的操作,对于 uu 的儿子 vv

Prev=max{Prev,Addv+Preu},Addv=Addv+AdduPre_v=\max\{Pre_v,Add_v+Pre_u\},Add_v=Add_v+Add_u,这很好理解

考虑上传信息的操作 ,对于 vv 的父亲 uu

mxu=max{mxv},hmxu=max{hmxv}mx_u=\max\{mx_v\},hmx_u=\max\{hmx_v\}​​

如何同时处理区间覆盖操作呢?

我们考虑对于一个节点的操作序列,在进行第一次区间赋值操作后之后未下传之前的所有其它操作都会成为区间赋值

我们其实可以理解为多增加一种操作序列表示区间赋值

那么我们多维护一个 IscovuIscov_u 表示节点 uu 是否有赋值的操作、 covucov_u​,表示赋值操作的对象以及 hcovuhcov_u,表示历史最大 covucov_u

现在此题就解决了!时间复杂度 O(mlogn)O(m\log n)

Problem 2

对于一个序列,支持区间减后对 0 chkmax,区间加,区间赋值,询问单点值,询问单点历史最大值

【清华集训2015】V

做法

我们考虑操作的形式

x=max{xa,0},x=x+a,x=ax'=\max\{x-a,0\},x'=x+a,x'=a

由于第一种操作形式特殊,我们试图把三种操作规约起来

x=max{x+a,b}x'=\max\{x+a,b\}

那么操作一的 a,ba,b​ 对显然是 (a,0)(-a,0)​,操作二则是 (a,inf)(a,-inf)​,操作三为 (inf,a)(-inf,a)

我们现在就把所有操作转化为 (a,b)(a,b) 的运算了

考虑定义 (a,b)+(c,d)(a,b)+(c,d)

x=max{max{x+a,b}+c,d}x'=\max\{\max\{x+a,b\}+c,d\}​,改写成 x=max{x+a+c,max{b+c,d}}x'=\max\{x+a+c,\max\{b+c,d\}\}

所以 (a,b)+(c,d)=(a+c,max{b+c,d})(a,b)+(c,d)=(a+c,\max\{b+c,d\})

定义 max{(a,b),(c,d)}\max\{(a,b),(c,d)\}

x=max{max{x+a,b},max{x+c,d}}x'=\max\{\max\{x+a,b\},\max\{x+c,d\}\} 改写成 x=max{x+max{a,c},max{b,d}}x'=\max\{x+\max\{a,c\},\max\{b,d\}\}

所以 max{(a,b),(c,d)}=(max{a,c},max{b,d})\max\{(a,b),(c,d)\}=(\max\{a,c\},\max\{b,d\})

此时我们直接像 Problem 1 一样维护操作序列总和、操作序列前缀和最大值即可

时间复杂度 O(mlogn)O(m\log n)

实际上,这个问题也支持区间询问最大值与历史最大值,考虑把初始序列看成操作即可