Unverified Commit cb18ca18 authored by Haoshen Zhong's avatar Haoshen Zhong Committed by GitHub
Browse files

style: update segment.md

parent 68e131fd
Loading
Loading
Loading
Loading
+16 −21
Original line number Diff line number Diff line
## 写在前面

线段树是个好东西啊 QwQ
线段树是个好东西啊

OI 中最常用的数据结构之一,不学不行啊 OvO
OI 中最常用的数据结构之一,不学不行啊

## 线段树是什么

> 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为 $O(\log N)$ 。而未优化的空间复杂度为 $2N$ ,因此有时需要离散化让空间压缩。——From 度娘
> 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为 $O(\log N)$ 。而未优化的空间复杂度为 $2N$ ,因此有时需要离散化让空间压缩。——via 百度

反正就是一种可以在很短的时间内对某个区间进行操作的数据结构。

@@ -21,7 +21,7 @@ OI 中最常用的数据结构之一,不学不行啊 OvO

想要建立一棵线段树,不理解它的结构、原理是肯定行不通的。

下面我来举个子:
下面我来举个子:

我们有个大小为 $5$ 的数组 $a=\{10,11,12,13,14\}$ 要进行区间求和操作,现在我们要怎么把这个数组存到线段树中(也可以说是转化成线段树)呢?我们这样子做:设线段树的根节点编号为 $1$ ,用数组 $d$ 来保存我们的线段树, $d[i]$ 用来保存编号为 $i$ 的节点的值(这里节点的值就是这个节点所表示的区间总和),如图所示:![](./images/segt1.png)

@@ -29,14 +29,13 @@ OI 中最常用的数据结构之一,不学不行啊 OvO

通过观察我们不难发现, $d[i]$ 的左儿子节点就是 $d[2\times i]$ , $d[i]$ 的右节点就是 $d[2\times i+1]$ 。进一步观察,可以看出如果 $d[i]$ 表示的是区间 $[s,t]$ (即 $d[i]=a[s]+a[s+1]+ \cdots +a[t]$ ) 的话,那么 $d[i]$ 的左儿子节点表示的是区间 $[s, \frac{s+t}{2} ]$ , $d[i]$ 的右儿子表示的是区间 $[ \frac{s+t}{2} +1,t]$ 。

为什么要这样表示呢?因为线段树利用了二分的思想,线段树实际上是个二叉树,这些不懂的话就无法理解线段树了,所以如果不明白二分或者二叉树的话……建议去问问度娘
为什么要这样表示呢?因为线段树利用了二分的思想,线段树实际上是个二叉树,这些不懂的话就无法理解线段树了,所以如果不明白二分或者二叉树的话,可以去查查

具体要怎么用代码实现呢?

我们继续观察,有没有发现如果 $d[i]$ 表示的区间大小等于 $1$ (区间大小指的是区间包含的元素的个数,即 $a$ 的个数)的话(设 $d[i]$ 表示区间 $[s,t]$ ,它的区间大小就是 $t-s+1$ ,不信你看上面的图),那么 $d[i]$ 所表示的区间 $[s,t]$ 中 $s$ 肯定等于 $t$ (不信你还是看图),且 $d[i]=a[s]$ (当然也等于 $a[t]$ )。
为什么要讲这个东西呢?你没发现这个是个递归边界吗?

O(∩\_∩)O 哈哈~

 **思路如下:** ![](./images/segt2.png)![](./images/segt3.png)![](./images/segt4.png)

@@ -86,12 +85,8 @@ void build(int s, int t, int p) {
(发博客累死了无聊一下)
如果要查询区间 $[1,5]$ 的和,那直接获取 $d[1]$ 的值( $60$ )即可。那如果我就不查询区间 $[1,5]$ ,我就查区间 $[3,5]$ 呢?

 **Σ(⊙▽⊙"a** 

懵 B 了吧。但其实呢我们肯定还是有办法的!

 **<(~ˇ~)/** 

你要查的不是 $[3,5]$ 吗?我把 $[3,5]$ 拆成 $[3,3]$ 和 $[4,5]$ 不就行了吗?

具体思路见代码:
@@ -139,9 +134,9 @@ int getsum(int l, int r, int s, int t, int p) {

### 线段树的区间修改与懒惰标记

区间修改是个很有趣的东西 o(╯□╰)o……你想啊,如果你要修改区间 $[l,r]$ ,难道把所有包含在区间[l,r]中的节点都遍历一次、修改一次?那估计这时间复杂度估计会上天 |(\*′口\`)。这怎么办呢?我们这里要引用一个叫做 **「懒惰标记」** 的东西。
区间修改是个很有趣的东西……你想啊,如果你要修改区间 $[l,r]$ ,难道把所有包含在区间[l,r]中的节点都遍历一次、修改一次?那估计这时间复杂度估计会上天。这怎么办呢?我们这里要引用一个叫做 **「懒惰标记」** 的东西。

我们设一个数组 $b$ , $b[i]$ 表示编号为 $i$ 的节点的懒惰标记值。啥是懒惰标记、懒惰标记值呢?(O_O)? 这里我再举个栗子(原创小故事我真有才哈哈哈 (◡ᴗ◡✿))
我们设一个数组 $b$ , $b[i]$ 表示编号为 $i$ 的节点的懒惰标记值。啥是懒惰标记、懒惰标记值呢?这里我再举个例子

> A 有两个儿子,一个是 B,一个是 C。
>
@@ -149,11 +144,11 @@ int getsum(int l, int r, int s, int t, int p) {
>
> 理论上来讲 A 应该把两个红包分别给 B 和 C,但是……缺钱嘛,A 就把红包偷偷收到自己口袋里了。
>
> A 高兴地说:「我现在有 $2$ 份红包了!我又多了 $2\times (1000000000000001\bmod 2)=2$ 圆了!哈哈哈~」
> A 高兴地说:「我现在有 $2$ 份红包了!我又多了 $2\times (1000000000000001\bmod 2)=2$ 圆了!哈哈哈~」
>
> 但是 A 知道,如果他不把红包给 B 和 C,那 B 和 C 肯定会不爽然后导致家庭矛盾最后崩溃,所以 A 对儿子 B 和 C 说:「我欠你们每人 $1$ 份 $(1000000000000001\bmod 2)$ 圆的红包,下次有新红包给过来的时候再给你们!这里我先做下记录……嗯……我钱你们各 $(1000000000000001\bmod 2)$ 圆……」
>
> 儿子 B、C 有点恼怒:「可是如果有同学问起我们我们收到了多少红包咋办?你把我们的红包都收了,我们还怎么装 ×?」
> 儿子 B、C 有点恼怒:「可是如果有同学问起我们我们收到了多少红包咋办?你把我们的红包都收了,我们还怎么装?」
>
> 父亲 A 赶忙说:「有同学问起来我就会给你们的!我欠条都写好了不会不算话的!」
>
@@ -176,7 +171,7 @@ int getsum(int l, int r, int s, int t, int p) {
![](./images/segt12.png)

注:这里 D 表示当前节点的值(即所表示区间的区间和)。
为什么节点 A 的 D 是 $2\times (1000000000000001\bmod 2)$ 呢?原因很简单。节点 A 表示的区间是 $[1,2]$ ,一共包含 $2$ 个元素。我们是让 $[1,2]$ 这个区间的每个元素都加上 $1000000000000001\bmod 2$ ,所以节点 A 的值就加上了 $2\times (1000000000000001\bmod 2)$ 咯 =~ω~=
为什么节点 A 的 D 是 $2\times (1000000000000001\bmod 2)$ 呢?原因很简单。节点 A 表示的区间是 $[1,2]$ ,一共包含 $2$ 个元素。我们是让 $[1,2]$ 这个区间的每个元素都加上 $1000000000000001\bmod 2$ ,所以节点 A 的值就加上了 $2\times (1000000000000001\bmod 2)$ 咯。

如果这时候我们要查询区间 $[1,1]$ (即节点 B 的值)怎么办呢?不是说了吗?如果 B 要用到的时候,A 就把它欠的还给 B!

@@ -247,7 +242,7 @@ int getsum(int l, int r, int s, int t,
}
```

你有没有发现区间查询和区间修改很像吗?(...^\_\_^...)
你有没有发现区间查询和区间修改很像吗?

嘻嘻……其实平时我打线段树区间修改和查询我都是打一份,另一份复制黏贴以后再稍作修改就行了。

@@ -307,7 +302,7 @@ int getsum(int l, int r, int s, int t, int p) {

### LUOGU P3372【模板】线段树 1

[传送门 =~ω~=](https://www.luogu.org/problem/show?pid=3372)
[传送门](https://www.luogu.org/problem/show?pid=3372)

代码:

@@ -370,7 +365,7 @@ int main() {

### LUOGU P3373【模板】线段树 2

[传送门 =~ω~=](https://www.luogu.org/problem/show?pid=3372)
[传送门](https://www.luogu.org/problem/show?pid=3372)

代码:

@@ -512,13 +507,13 @@ int main() {

### CODEVS 线段树练习(这是一个系列)

[传送门 =~ω~=](http://codevs.cn/problem/?q=%E7%BA%BF%E6%AE%B5%E6%A0%91%E7%BB%83%E4%B9%A0)
[传送门](http://codevs.cn/problem/?q=%E7%BA%BF%E6%AE%B5%E6%A0%91%E7%BB%83%E4%B9%A0)

具体题解去百度吧QwQ...
具体题解去百度吧...

### HihoCoder 1078 线段树的区间修改

[传送门 =~ω~=](https://cn.vjudge.net/problem/HihoCoder-1078)
[传送门](https://cn.vjudge.net/problem/HihoCoder-1078)

代码: