普通线段树

普通线段树

它的精髓在于懒标记的使用

一些普通操作

  • 区间加减, 区间求 \(gcd\)

因为 \(gcd(a, b, c) = gcd(a, b - a, c - b)\)

所以后边在区间加减的时候是不变的, 所以可以维护差分做到, 就是单点修改, 区间 \(gcd\)

然后 \(a\) 就可以通过区间修改, 单点查询做到

  • 最大子段和

思想就是维护前缀后缀去找到跨过这两个区间的

[ZJOI2007] 捉迷藏

区间最值

这个在2016国家集训队论文中提到过, 吉司机线段树

区间最值形如

\(a_i = min(a_i, x)\)

它通过将一段区间里的数分为 \(max\) 和 非 \(max\)

递归到 \(se < x < max\) 时, 只对 \(max\) 类数进行维护, 复杂度如果只有区间最值可以做到 \(nlogn\)

如果 有很多种操作, 是 \(nlog^2n\)

这样, 我们就可以把区间最值操作变成区间加减操作

我们将数分成三类 \(max, min\) , 其它

然后分别去维护三类数的懒标记, \(max\)\(min\) 是平级的

  1. \(\forall i \in [l, r], A_i = min(A_i, x)\)

  2. \(\forall i \in [l, r], B_i = min(B_i, x)\)

  3. \(\forall i \in [l, r], A_i = A_i + x\)

  4. \(\forall i \in [l, r], B_i = B_i + x\)

  5. \(\forall i \in [l, r], \max (A_i + B_i)\)

区间最值操作转成区间加减, 那么 \(A_i + B_i\) 就变成了四类数

分别维护就可以了

  • 区间最值, 区间加减, 区间求 \(gcd\)

乍一看可能感觉很难, 但是我们仍然是区间最值转区间加减

考虑将非最大值与最大值看成了两个连续的区间, 注意: 顺序无所谓

具体地来说, 我们维护 \(s, t\) 表示非最大值的开头和结尾, 当两个子树合并的时候, 比较最大值

然后按照 \(s_l, ..., t_l, s_r, ..., t_r, max_r\) 假设 \(max_l > max_r\)

这样拼接起来, 那么区间 \(gcd = gcd(gcd_l, s_r - t_l, max_r - t_r, gcd_r)\)

然后维护就可以了


最主要的思想还是 区间最值操作变成区间加减操作

但是注意的是这个操作方法的限制是我们要维护的东西没有下标顺序的限制

历史最值

可以使用懒标记的求单点/区间 \(max\)

\(\forall i \in [l, r], A_i = A_i + x\)

\(\forall i \in [l, r], \max(A_i)\)

\(\forall i \in [l, r], \max(B_i)\)

\(B_i = \max(A_i)\)

区间加法, 然后维护懒标记的最大值

因为 \(A_i + x_1 + x_2 + x_3...\)

找寻这个过程中的最大值, 显然只需要找到 \(A_i\) 固定时, \(x_1 + x_2 + x_3..\)的前缀和最大值

然后再开一个懒标记维护这个东西就可以了

\(\forall i \in [l, r], A_i = \max(A_i - x, 0)\)

\(\forall i \in [l, r], A_i = A_i + x\)

\(\forall i \in [l, r], A_i = x\)

\(\forall i \in [l, r], \max(A_i)\)

\(\forall i \in [l, r], \max(B_i)\)

\(B_i = \max(A_i)\)

我们可以把 \(max\) 理解成一次区间加法, 再来一次区间最值

但这太不优美了

我们考虑第一个函数所提供的操作, 我们可以设这样一个函数操作 \(f(x) = \max(x + a, b)\)

操作一就是 \((-x, 0)\) 操作二就是 \((x, -inf)\) 操作三就是 \((-inf, x)\)

然后两个函数操作就是, \((a, b), (c, d) \rightarrow (a + c, \max(b + c, d))\)

可以使用懒标记维护

通过画图, 发现两个函数取 \(max\) 后的形态还是一样的形态, 所以也可以通过二元组来维护, 这个就可以求历史最值了

他的函数图像是单调的, 所以最大值永远是最大值, 然后就做完了, 复杂度 \(O(nlogn)\)

很强的一道题


这些懒标记的题都可以归为双半群模型, 可见下


不可以使用懒标记的求单点/区间 \(max\)

如果把上边的题改成

\(\forall i \in [l, r], \min(A_i)\)

\(\forall i \in [l, r], \min(B_i)\)

我们发现函数取 \(min\) 没有那么的优美, 这就是不同向带来的坏处, 上一次见这个恶心的东西是反向四边形不等式, 当时通过加一个 \(log\) 解决的, 这里也需要

我们把最值按照上边的处理方式, 变成区间加减问题, 复杂度就变成了 \(O(nlog^2n)\)

无区间最值操作的区间求和问题

  • 历史最小值

\(C_i = A_i - B_i\)

显然要求 \(C_i\) 尽可能的大, 一次区间操作就是 \(C_i + x\) , 一次修改 \(B_i = A_i\) 就是 \(C_i = 0\)

所以就是 \(\max\{C_i + x, 0\}\)

这是一个熟悉的操作 , 可以通过区间最值做到 \(n log^2n\)

  • 历史最大值

同上 \(C_i = A_i - B_i\)

\(\min\{ C_i + x, 0\}\)

做到 \(nlog^2n\)

  • 历史和

这里我想用树状数组的区间求和区间查询的思想来解释

我们考虑一次区间加法的操作的贡献 \((n - t) \times v\)

因为 \(n\) 我们是未知的, 所以我们可以拆开 \(n \times v - t \times v\)

然后 \(n\) 可以乘一个 \(sum\) 来计算前边部分, 后边就是一个定值了

有区间最值操作的区间求和问题

我们通过把区间最值换成针对最大/小值的加减操作, 完成多一个 \(log\) 来完成这种问题

具体地, 维护 \(A\) 最大值序列的 \(C\) 的最大值与非最大值, \(A\) 非最大值序列的 \(C\) 的最大值与非最大值

双半群模型

深度好文

数值集合\(: D\), 懒标记集合 \(T\)

给定:

  • 一个半群 \((D,+)\) 和一个幺半群 \((T,\times)\),满足:
  1. \(D \times T \rightarrow D\)
  2. 结合律:\(\forall a\in D\)\(b,c\in T\)\((a\times b)\times c=a\times(b\times c)\)
  3. 分配律:\(\forall a,b \in D\)\(c\in T\)\((a+b)\times c=a\times c+b\times c\)

这就是双半群模型, 可以使用懒标记来维护

有时候我们可以使用矩阵来完成这个伟大的操作, 因为两个矩阵本身就满足上边的性质

有时候矩阵乘法可以变成广义矩阵乘法, 矩阵加法变成 \(max\)

据此,我们可以得出一个构造流程:

  1. 构造可行的信息 \(D\),检验是否满足 \(D+D\rightarrow D\),并得出转移形式。
  2. 通过计算各个操作对信息 \(D\) 的影响,总结出标记 \(T\),检验是否满足 \(D\times T \rightarrow D\),并得出转移形式。
  3. 通过计算验证标记 \(T\) 是否满足 \(T\times T \rightarrow T\),并得出转移形式。
  4. 如果出现不满足的情况,从 \(1\) 重新开始。

下边给出一些例题

有三个序列 \(a,b,c\),长度均为 \(n\), 又有 \(7\) 种操作,共 \(m\) 个,分别为:

  1. \(a_i=a_i+b_i\)
  2. \(b_i=b_i+c_i\)
  3. \(c_i=c_i+a_i\)
  4. \(a_i=a_i+k\)
  5. \(b_i=b_i\times k\)
  6. \(c_i=k\)
  7. 查询 \(\sum_{i=l}^{r} a_i,\sum_{i=l}^{r} b_i,\sum_{i=l}^{r} c_i\)

从查询入手, 需要维护 \(suma_i, sumb_i, sumc_i\)

我们尝试写一下矩阵, 如何把 \(k\) 插进去, 所以设一个常数 \(1\)

\( \begin{bmatrix} a_i & b_i & c_i & 1\\0 & 0& 0& 0\\0 & 0& 0& 0\\0 & 0& 0& 0\end{bmatrix} \)

然后这几个操作用矩阵表示还是比较简单的

矩阵满足双半群性质, 可以使用懒标记

我们定义 \(f_i\) 为第 \(i\) 个斐波那契数。

并且给定一个 \(n\) 个数的序列 \(a\)。有 \(m\) 次操作,操作有两种:

  1. \(a_l\sim a_r\) 加上 \(k\)
  2. \(\displaystyle\left(\sum_{i=l}^r f_{a_i}\right)\bmod (10^9+7)\)

\(1\le n,m\le 10^5\)\(1\le a_i\le 10^9\)

斐波那契可以通过矩阵转移, 加法就是转移 \(k\) 次, 然后就做完了


但是我们发现矩阵是比较慢的, 所以我们想要把矩阵中非 \(0\) 的不去维护, 只去维护本质不同的

这个是可以做区间历史和的

给定序列 \(a\)\(s\),有两个操作。

  1. 给出一段区间 \([l,r]\),令 \(\forall i\in[l,r],a_i=a_i+k\)
  2. \(\forall i \in [1,n],s_i=s_i+a_i\)

从询问入手, 我们要维护 \(suma_i, sums_i\) 因为有加法所以维护 \(cnt\)

合并是好合并的

两个操作

  1. 令函数 \(f\) 代表区间加操作,则有 \(f((suma,sums,cnt))=(suma+cnt\times x,sums,cnt)\),其中 \(x\) 是我们要加减的数
  2. 令函数 \(g\) 代表区间更新操作,则有 \(g((suma,sums,cnt))=(suma,sums+y \times suma,cnt)\),其中 \(y\) 是更新次数

将一个 \(D\) 运算两种不同的操作

\(g(f((suma,sums,len)))=(suma+cnt\times x,sums+y\times suma+cnt\times x \times y)\)

不难发现我们的懒标记如何设计

\((suma,sums,cnt) \times (taga, ypd, cnta)=(suma+cnt\times taga,sums+upd\times suma+cnt\times cnta)\)

\(T \times T\) 如何计算? 分别计算

\(D \times T \times T\)

然后可以看出来, \(T \times T\) 是什么样子的

然后就可以做了, 非常强

给定序列 \(a\)\(b\)\(s\),有三个操作。

  1. 给出一段区间 \([l,r]\),令 \(\forall i\in[l,r],a_i=a_i+k\)
  2. 给出一段区间 \([l,r]\),令 \(\forall i\in[l,r],b_i=b_i+k\)
  3. \(\forall i \in [1,n],s_i=s_i+a_i\times b_i\)

仍然是根据询问维护东西 \(s, a, b, sum, len\)

写出操作的函数

  1. 令函数 \(f\) 代表区间加序列 \(a\) 操作,则有 \(f((a,b,sum,s,len))=(a+len\times x,b,sum+b\times x,s,len)\),其中 \(x\) 是我们要加减的数。
  2. 令函数 \(g\) 代表区间加序列 \(b\) 操作,则有 \(g((a,b,sum,s,len))=(a,b+len\times y,sum+a\times y,s,len)\),其中 \(y\) 是我们要加减的数。
  3. 令函数 \(h\) 代表区间更新操作,则有 \(h((a,b,sum,s,len))=(a,b,sum,s+sum\times z,len)\),其中 \(z\) 是更新次数。

我们把它们复合起来,得到 \(F(x) = h(g(f(x)))\),也就得到了 \(F((a,b,sum,s,len))=(a+len\times x,b+len\times y,sum+a\times y+b\times x,s+zx\times b+zy\times a+len\times zxy)\)

如果我们仍然按照上边的方法来求解, 发现非常的难受, 麻烦

所以我们借助于矩阵来实现, 我们的 \(D \times T\) 就是矩阵

\[\begin{bmatrix}a & b & sum & s& len\\0&0&0&0&0\\0&0&0&0&0\\0&0&0&0&0\\0&0&0&0&0\end{bmatrix}\times\begin{bmatrix}1&0&y&zy&0\\0&1&x&zx&0\\0&0&1&z&0\\0&0&0&1&0\\x&y&xy&zxy&1\end{bmatrix} \]

两个矩阵相乘得到

\[\begin{bmatrix} 1&0&y+y'&zy+z'y+(zy)'&0\\0&1&x+x'&zx+z'x+(zx)'&0\\0&0&1&z+z'&0\\0&0&0&1&0&\\ x+x'&y+y'&(x+x')(y+y')&zxy+x(zy)'+(zx)'y+z'xy+(zxy)'&1\end{bmatrix} \]

我们找到本质不同项, 然后转而用数组来维护

六元组 \((x,y,z,zx,zy,zxy)\)

\((x,y,z,zx,zy,zxy)\times (x',y',z',(zx)',(zy)',(zxy)')=(x+x',y+y',z+z',zx+z'x+(zx)',zy+z'y+(zy)',zxy+x(zy)'+(zx)'y+z'xy+(zxy)')\)

然后就做完了


给出一些使用广义矩阵乘法去维护上文中的 可以使用懒标记的求单点/区间 \(max\)

  1. \(\forall i \in [l, r] A_i = k\)
  2. \(\forall i \in [l, r] A_i = A_i + k\)
  3. 询问 \(\forall i \in [l, r] \max \{ A_i\}\)
  4. 询问 \(\forall i \in [l, r] \max \{ B_i\}\)

\(B_i = \max \{ A_i, B_i\}\)

\(max\) 不难想到利用广义矩阵乘法

区间赋值操作要求我们再维护一个常数项

\( \begin{bmatrix} -\infty & -\infty & k \\ -\infty & 0 & k\\ -\infty & -\infty & 0 \end{bmatrix} \begin{bmatrix} a \\ b \\ 0 \end{bmatrix} = \begin{bmatrix} k \\ \max\{b, k\} \\ 0 \end{bmatrix} \)

\( \begin{bmatrix} k & -\infty & -\infty \\ k & 0 & -\infty\\ -\infty & -\infty & 0 \end{bmatrix} \begin{bmatrix} a \\ b \\ 0 \end{bmatrix} = \begin{bmatrix} a + k \\ \max\{b, a + k\} \\ 0 \end{bmatrix} \)

区间最值操作通过上边的套路可以变成区间加减, 然后区间求和分开来维护, 区间历史最值就可以通过广义矩阵乘法来实现了

posted @ 2025-02-16 19:18  d3genera7e  阅读(30)  评论(0)    收藏  举报