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

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

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

乘的时候相乘交换即可。

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

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

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

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

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

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

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

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

曼哈顿距离:\(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(n\log^3 n).\)

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

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

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

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

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

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

总复杂度 \(O(n\log^2 n).\)

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

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

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

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

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

\(\max-\min+1-(r-l+1)\leq k,\not\exists a_i,a_j,a_i=a_j\)

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

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

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

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

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

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

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

式子大概是 \(\min\left\{ |a_i-a_j|(i,j\in[l,r])\right\}\)

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

\(R\to 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)[i\leq j]\)

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

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

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

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

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

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

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

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

posted @ 2021-07-01 16:02  Refined_heart  阅读(74)  评论(0编辑  收藏  举报