数据结构学习笔记

1 线段树

1.1 什么是线段树?

线段树在算法竞赛中常用于区间操作。时间复杂度$O(\log n)$。

1.2 线段树的应用

1.2.1 线段树的基本应用

顾名思义,区间修改,区间查询。

1.2.2 线段树最大字段和问题上的运用

问题描述:

  • 操作一:进行单点修改。

  • 操作二:给定 $l$ 和 $r$,问 $[l, r]$ 中的最大的子段和是多少?

为了解决这个问题,我们需要引入的懒标记有如下几个

  • $maxv$ 区间最大子段和,也就是问题的答案。
  • $sumv$ 区间的和。
  • $maxnl$ 包含左端点的区间最大子段和。
  • $maxr$ 包含右端点的区间最大子段和。

在修改时

先更新 $maxv$。

若 $maxl < 0 \and maxr < 0$(情况一),$maxv = \max{maxl,maxr}$ 。题目中要求必须要取一个元素,不能不取。

若情况一不成立,则说明 $maxl < 0 \or maxr < 0$ 为真(情况二),此时需要将 $maxv$ 归 $0,然后将$ $maxv$ 就可以加上 $maxl,maxr$ 中为正的值。

也有可能以上的情况都不是最大子段和,最后的 $maxv = \max{maxv,maxv(左儿子),maxv(右儿子)}$。特别说明,以上两种情况中的 $maxl$ 和 $maxr$ 分别是其右左儿子的信息。

接着更新 $maxr$ 和 $maxl$ 。

因为线段树需要合并两个区间,可以得出 $maxl$ 为左儿子的 $maxl$ 与左儿子 $sumv$ 和右儿子 $maxl$ 和的最大值。而 $maxr$ 为右儿子的 $maxr$ 与右儿子 $sumv$ 和左儿子 $maxr$ 的最大值。该节点的 $sum$ 为其左右儿子 $sum$ 的和。

在查询时

若查询区间覆盖这一节点,则返回这一节点的信息;若完全覆盖了其中一个儿子,则返回覆盖到的儿子的信息;若没有完全覆盖一个儿子,通过合并的方式查询覆盖到的区间。最后返回的 $maxv$ 即为一次查询的最终答案。

模板题代码

1.2.3 01 区间翻转 + 最长子段和

Code

1.3 利用树状数组代替线段树

众所周知线段树的常数大,代码难写,而在考试中经常出现时间不足从而写不出线段树造成悲剧的情况。但现在,我们可以使用树状数组这种代码好写的数据结构代,完成类似于线段树的区间修改区间查询功能。

为了赋予树状数组这一伟大的使命,我们需要两个树状数组,$t1、t2$。

令 $t[ i ] = a[i] - a[i-1]$ (差分,其中 $a$ 为给定的数组),易得:

$$\sum_{i=1}^{n} t1[i]=a[n]$$

于是我们可以进行转换:

$$=\sum_{i=1}^{n}a[i] = \sum_{i = 1}^{n}\sum_{j=1}^{i}t1[j]$$

由加法交换律和加法结合律可得:

$$=\sum_{i =1}^{n} t1[i]\times(n-i+1)$$

由乘法分配律可得:

$$=n\times \sum_{i=1}^{n} t1[i]- \sum_{i = 1}^{n}t1[i]\times (i - 1)$$

令 $t2[i] = t1[i]\times(i-1)$ ,最终可以得到:

$$=n\times \sum_{i=1}^{n} t1[i]- \sum_{i = 1}^{n}t2[i]$$

对于一次添加操作:

void modif(int x, int v) {
    t2.modify(x, (x - 1) * v); t1.modify(x, v);
}

对于一次查询操作:

int qure(int y) {
    return t1.qurey(y) * (y) - t2.qurey(y);
}

那么对于一次区间查询,则有:

int qurey(int l, int r) {
    return qure(r) - qure(l  - 1);
}

同理,区间修改:

void modify(int l, int r, int v) {
    modif(l ,v); modif(r + 1, -v);
}

完整代码

1.4 线段树合并

线段树合并是通过一种相当暴力的手法把两颗线段树的节点合并,常用于维护书图上树上问题。

进行操作需要用到动态开点线段树,有点可持久化的思想。

似乎比较常规,没有什么需要特特别注意的。

合并:

    int merge(int a, int b, int l = 1, int r = -1) {
        if (r == -1) r = n;
        if(!a || !b) return max(a, b);
        if(l == r) {
            sum[a] += sum[b];
            return a;
        }    
        int mid = l + r >> 1;
        ls[a] = merge(ls[a], ls[b], l, mid);
        rs[a] = merge(rs[a], rs[b], mid + 1, r);
        update(a);
        return a;
    }
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇