【学习笔记】 线段树题目选讲

1. 区间最大子段和,区间乘 (-1,n,mleq 10^5.)

考虑维护从左边开始、从右边开始的最大、最小子段和以及区间最大、最小子段和。

乘的时候相乘交换即可。

2. 单点修改,求区间单调栈大小。

楼房重建 的加强版:区间询问。

考虑维护区间信息(合并信息):类比楼房重建的 pushup 写法,先递归处理出左区间以及其 max 信息,然后递归右边处理。

考虑递归处理右边区间,把他分成两个子区间,并分类讨论:设左区间是 (L) 右区间是 (R) ,对 (max L) 分类讨论:

(max l) 小于左边 (max) 则直接递归右子区间

否则递归左子区间,同时发现剩下的区间依旧是之前维护过的 (max L o max R.) 这个可以直接差分计算。

复杂度 (O(qlog^2 n).) 做法是一样的。

3. 给定序列 (left{A ight},A_i o left{c,x,y ight}) 分别表示颜色和坐标,要求区间询问颜色不同的曼哈顿距离最大一对点的距离。

曼哈顿距离:(dis=|x_i-x_j|+|y_i-y_j| =) ((x_i-x_j+y_i-y_j), (x_i-x_j+y_j-y_i) ,(x_j-x_i+y_i-y_j),(x_j-x_i+y_j-y_i))

对最大值 (w_i=x_i+y_i) (四种)维护它的颜色以及次大值和颜色,最小值同理。(要求最大值与次大值颜色不同)

考虑最大值与最小值匹配,如果颜色相同则考虑两边最大值与次小值、次小值与最大值匹配即可。

4.单点修改,区间 (mex)

原题: 19ICPC徐州H

先考虑目前的 (mex) ,记为 (s,) 若区间内的数要小于等于 (s+1) 那么它们都可以给 (s) 做贡献。(即 它们可以和 (s) 拼接起来,构成新的数且不出现空位 (mex) .)

那么每次这样的加和之后 (s) 的增长速率是倍增级别的。那么我只需 (log n) 次的询问就可以把区间做完。

而区间信息怎么维护?权值线段树带可持久化即可。维护区间和。每次找到区间上 ([pre_s,s+1]) 上的数并加上它的和更新 (s) 即可。

带修改就套个树状数组

复杂度 (O(nlog^3 n).)

考虑值域分块,分成 ([2^{i-1}\, 2^i).) (s) 的增加过程是一些倍增长度的线段拼成的。而且对于任意一个段, (s) 要么全加要么一个都不加。

证明:若存在一个值使得 (s) 可以加上这个数,则这个数最小为 (2^i,) 满足 (2^ileq s o s>2^i o s+2^i> 2^{i+1})

所以要么一个段全加要么一个都不加。

对每一个段维护线段树,维护区间最小值以及和。每次询问区间的最小值和 sum 并决定是否跳跃。

(log n) 段,询问是 (O(qlog^2 n).) 修改 (O(qlog n))

对每一个值域维护的线段树是以序列下标为序的线段树,询问是对应的区间进行询问,修改是找到值对应的值域树,再去修改对应位置。

总复杂度 (O(nlog^2 n).)

7. 给定序列以及 (k,n) 求一个最长区间使得它加入最多 (k) 个数后排序后是一个公差为 (d) 的等差数列。

这样的区间满足的条件:(mod d) 相同,且没有重复的数。

那么先按照模数分块,分成若干可能的区间。那么对每个区间,设 (c=xmod d,x o frac{x-c}{d},) 公差被转化成 (1.) (缩放操作)

那最小要加入的数就是: (max -min +1 -(r-l+1)) (预计最短区间减去目前的区间长度)

那枚举右端点,找到最小左端点满足:

(max-min+1-(r-l+1)leq k, otexists a_i,a_j,a_i=a_j)

无重复可以在枚举 (l) 的时候维护下界。

第一个条件:考虑维护(w_i=max [l,r]-min[l,r]+l,) 求最左边的 (w_ileq k+r)

维护一个单调栈,将 (max [l,r]) 分成若干个段。每次 (R) 增加的时候,判断栈顶弹出元素将其覆盖的区间消除影响,并将新加入元素的影响加入线段树。

影响指对应一段区间的最大值是一定的。

对最小值维护是同理的。我们可以直接在线段树上维护(w_i=max -min +i) 对应维护两个单调栈,每次有一个单调栈更新的时候,对应的 (w) 的影响是一样的并且是连续的一段区间,进行区间加减即可。

查询最左端的 (w_ileq k+R) 可以维护一下线段树上的最小值,树上二分即可。

8.给定序列 多次询问区间最小差值。

式子大概是 (minleft{ |a_i-a_j|(i,jin[l,r]) ight})

从小到大枚举 (R) 用线段树(下标(i o ans_{l,r})), (R)增加意味着 (l) 需要修改。

(R o R+1) 时,考虑更新。

记左边第一个大于 (a[R]) 的值是 (a[x]). 考虑找到(a[R]<a[y]<a[x],a[y]-a[R]<a[x]-a[y],x<y.)

这样, (x) 能更新的区间是([y+1,x]) (y) 能更新的区间是 ([1,y].)

考虑找 (y) 的过程:观察到 (y) 在位置上和值域上都有限制,将差的限制转换成值域的限制后,考虑对序列建立值域线段树,并可持久化,在上面二分。

同时写一个维护贡献的线段树维护区间询问即可。

主席树对值域建立维护一下siz,通过做差固定区间之后,维护siz二分找值。

9. 定义 (work(i,j)) 是在区间 ([1,n]) 的线段树上区间询问访问到的点的个数,求 (sum sum work(i,j)[ileq j])

若一个线段树节点会被访问到,需要:

  • ([l,r])([x,y]) 有交点

  • ([fal,far]) 不被 ([x,y]) 包含

第一种情况把多余的区间部分减去 (C_n^2) 就好了

第二种:分类讨论推式子。

对于一个线段树上的区间([l,r])(work(l,r)) 有一个函数 (f(x,y)) 是一个前文推出来的二次函数,对线段树上的节点预处理出这个东西。

在线段树上进行询问(为了避免掉和询问不相干的节点),发现一棵子树中的信息可以求和合并,维护子树和即可。

最终复杂度被优化成了一个区间询问。

原文地址:https://www.cnblogs.com/h-lka/p/14959202.html