学习笔记:线段树

发布时间 2023-05-16 20:50:39作者: f2021ljh

在已经掌握线段树的基本用法后的做题整理。给自己复习用的。

\(mid\) 表示 \((l+r)/2\)\(u\) 表示当前区间节点(父区间),\(ls,rs\) 分别表示当前区间的左、右子区间节点。

普通维护序列

P2023 [AHOI2009] 维护序列

修改:区间加,区间乘;询问:区间求和。

双倍经验:P3373 【模板】线段树 2

维护两个标记,加 add 和乘 mul。在 pushdown 函数中,要先乘再加,乘的时候把 add 标记也更新。

代码

P4513 小白逛公园

修改:单点加;询问:区间最大子段和。

考虑如何合并。一个区间 \([l,r]\) 的最大子段可能在 \([l,mid]\) 中或 \((mid,r]\) 中,也可能横跨左右两个子区间。

对于前一种情况,我们直接对子区间的答案取 \(\max\)。对于后一种情况,我们记录前缀最大值 \(lmx\) 和后缀最大值 \(rmx\),用 \(lmx_{ls}+rmx_{rs}\) 更新 \(u\) 的答案。

对于 \(lmx\) 的更新,可能在 \([l,mid]\) 中,也可能是 \([l,j]\),其中 \(j\in(mid,r]\)\(rmx\) 同理。

代码

P1471 方差

修改:区间加;询问:区间平均数,区间方差。

区间平均数 \(avg\) 即为区间和 \(sum\) 除以区间长度 \(len\),分别维护即可。

区间方差有点难搞,考虑区间加 \(k\) 时方差 \(dat\) 如何变化。对公式变形:

\[\begin{aligned} \sigma^2&=\frac1n\sum\limits_{i=1}^n\left(x_i-\overline x\right)^2 \\ &=-\overline x^2+\frac1n\sum x_i^2 \end{aligned} \]

于是只需要维护区间平方和 \(sum2\) 如何变化。

\[\sum(x_i+k)^2=\sum x_i^2+2k\sum x_i+nk^2 \]

代码

P6327 区间加区间sin和

修改:区间加;询问:区间 \(\sin\) 和,即 \(\sum_{i=l}^r\sin a_i\)

\[\begin{aligned} \sum_{i=l}^r\sin(a_i+k)&=\sum(\sin a_i\cos k+\cos a_i\sin k)\\ &=\cos k\sum\sin a_i+\sin k\sum\cos a_i \end{aligned} \]

因此我们还需要维护一个 \(\cos\) 和。维护方式同理。注意更新时要先临时保存原来的 \(\sin\) 和与 \(\cos\) 和。

注意区间加的懒标记需要开 long long

代码

**P7706 「Wdsr-2.7」文文的摄影布置

定义 \(\psi(x,y)=A_x+A_y-\min\limits_{x<i<y}\{B_i\}\)

修改:单点修改 \(A_i\)\(B_i\);询问:\(\max\limits_{l\le x<y\le r}\{\psi(x,y)\}\)(给定 \(l,r\))。

\(1\le n,m\le5\times10^5\)\(1\le A_i,B_i\le10^8\)

考虑如何合并。设 \(ans\) 为维护的答案。首先肯定是对 \(ans_{ls}\)\(ans_{rs}\)\(\max\)。然后考虑横跨左右子区间的情况,即 \(x\le mid,y>mid\)

讨论 \(\max B_i\) 在左子区间内还是右子区间内。

如果在左子区间内,我们转化式子:

\[\max\psi(x,y)=\max\left\{A_x-\min_{x<i\le mid}\{B_i\}\right\}+\max A_y. \]

注意到可以分成两部分,\(\max\left\{A_x-\min\limits_{x<i\le mid}\{B_i\}\right\}\) 只与左子区间有关,\(\max A_y\) 只与右子区间有关。

于是可以分别维护,设 \(ab_u=\max\limits_{l\le x\le r}\left\{A_x-\min\limits_{x<i\le r}\{B_i\}\right\}\)\(mxa_u=\max\limits_{l\le i\le r}A_i\)

考虑如何从子区间更新 \(ab_u\)。首先显然用 \(\max(ab_{ls},ab_{rs})\) 更新。

然后,考虑 \(x\in[l,mid]\),但使得 \(B_i\) 最小的 \(i\)\((mid,r]\) 内的情况。为了让两项都最大,我们用 \(\max\limits_{l\le x\le mid}A_x\) 减去 \(\min\limits_{mid<i\le r}B_i\)

于是还要维护区间最小值 \(mnb_u=\min\limits_{l\le i\le r}B_i\)

最后答案: \(ab_u=\max(ab_{ls},ab_{rs},mxa_{ls}-mnb_{rs})\)

如果在右子区间内,同理有:

\[\max\psi(x,y)=\max A_x+\max\left\{A_y-\min_{mid<i<y}\{B_i\}\right\} \]

同样维护区间 \(\max\left\{A_y-\min\limits_{mid<i<y}\{B_i\}\right\}\)\(\max A_x\) 即为 \(mxa_{ls}\)。设 \(ba_u=\max\limits_{l\le y\le r}\left\{A_y-\min\limits_{l<i<y}\{B_i\}\right\}\)

同理,有 \(ba_u=\max(ba_{ls},ba_{rs},mxa_{rs}-mnb_{ls})\)

然后就可以更新 \(ans_u\) 了。

具体见 代码

这个似乎有 \(l,r,x,y\) 的边界问题但我不知道怎么搞。

*CF438D The Child and Sequence

区间取模,单点修改,区间求和。

\(1\le n,m\le10^5\),值域 \(10^9\)

对于多次取模、多次除法、多次开方这样的问题,我们首先需要考虑:一个数 \(x\) 经过若干次操作,最后是否会保持为 \(0\),不管继续怎么操作都不变。

在本题中,我们可以发现,若 \(p\le x\),则有 \(x\bmod p\) 总小于 \(x/2\)。因此,对于每个数 \(x\),只需要取模 \(\log x\) 次就会变为 \(0\)

因此,我们不断地找区间内的最大值 \(mx\)\(\boldsymbol{p\le mx}\),则将 \(mx\)\(p\) 取模并更新区间最大值,然后对新的区间最大值重新执行该操作。单点修改和区间求和正常即可。

由于每个数最多取模 \(\log\) 次,因此时间复杂度为 \((n+m)\log n\log v\),其中 \(v\) 为值域大小。

代码

*P4344 [SHOI2015] 脑洞治疗仪

一个长度为 \(n\)\(0/1\) 序列,初始均为 \(1\)

  • 操作 0:将 \([l,r]\) 设为 \(0\)
  • 操作 1:把 \([l_0,r_0]\) 内所有的 \(1\) 拿出来(原位置变成 \(0\)),填在 \([l_1,r_1]\) 内所有的 \(0\) 位置上,且尽量靠前填,如果不够则不填,如果有多出来的 \(1\) 则直接扔掉;
  • 查询:\([l,r]\) 内最长的连续 \(0\) 的长度。

\(1\le n,m\le2\times10^5\)

对于操作 0,直接区间打标记即可。

对于操作 1,我们数出区间 \([l_0,r_0]\) 内 1 的个数,然后把区间全部设为 \(0\)(即操作 0),最后用这些 1 按照题目要求填进 \([l_1,r_1]\) 内。

具体地,如果左区间的空位数 \(x\) 大于等于当前要填的 \(1\) 的个数 \(y\),那么全部填到左区间;如果左区间的空位数 \(x\) 小于当前要填的 \(1\) 的个数 \(y\),那么把左区间全部设为 \(1\)(方法同操作 0),并且用 \(y-x\)\(1\) 填入右区间,这样就变成了一个子问题。

对于询问,我们考虑如何合并,讨论最大值是左、右还是横跨左和右即可。如上面的 P4513 小白逛公园。

要维护的有:标记、区间长度、\(1\) 的个数、前缀最长连续 \(0\)、后缀最长连续 \(0\)、区间最长连续 \(0\)

代码

维护某个值

实际上就是把题意所需要维护的东西转化,变成可以用线段树维护的序列上的问题。

P6864 [RC-03] 记忆

有一个括号串 \(S\),一开始 \(S\) 中只包含一对括号(即初始的 \(S\)()),接下来有 \(n\) 个操作,操作分为三种:

  1. 在当前 \(S\) 的末尾加一对括号(即 \(S\) 变为 S());

  2. 在当前 \(S\) 的最外面加一对括号(即 \(S\) 变为 (S));

  3. 取消第 \(x\) 个操作,即去除第 \(x\) 个操作造成过的一切影响(例如,如果第 \(x\) 个操作也是取消操作,且取消了第 \(y\) 个操作,那么当前操作的实质就是恢复了第 \(y\) 个操作的作用效果)。

每次操作后,你需要输出 \(S\) 的能够括号匹配的非空子串(子串要求连续)个数。

一个括号串能够括号匹配,当且仅当其左右括号数量相等,且任意一个前缀中左括号数量不少于右括号数量。

对于全部数据:\(1\leq n\leq 2\times 10^5\)\(op\in \{1,2,3\}\)\(1\leq x\leq n\),一个操作在形式上最多只会被取消一次(即所有 \(x\) 互不相同)。

单侧递归类问题

*P4198 楼房重建