码迷,mamicode.com
首页 > 编程语言 > 详细

树状数组预备

时间:2020-06-12 14:38:08      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:结构   span   查询   使用   二维   级别   code   ali   div   

<前言>

本次依旧是高手训练专题解析。

但与以往不同的是,这次会附上树状数组基础内容。

本篇为基础内容。


<树状数组>

在此贴出大哥的blog,有更加全面、系统的介绍。

什么是树状数组?怎么用树状数组?树状数组有什么应用?

什么是树状数组?

树状数组 是一种数据结构, 可以在\(O(log_2 n )\)时间内完成 **修改、查询 **等序列操作。

它可以处理区间、单点加&查询,以及前(后)缀最值

它利用了\(lowbit()\)运算的一些性质。

观察\(1...10\)\(\mathrm{lowbit}\)序列:

序号 \(lowbit\) 二进制
1 1 1
2 2 10
3 1 11
4 4 100
5 1 101
6 2 110
7 1 111
8 8 1000
9 1 1001
10 2 1010

容易发现\(lowbit\)就是在求最低位的1所代表的数。

而我们可以通过补码的性质快速求\(lowbit\)

#define lowbit(x) (x & (-x))

那么我们可以根据这个性质搞出一棵树(森林),使一些点对应一段区间。

大概长这样:(\(\mathrm{x}\)连向\(\mathrm{y}\)的边代表\(\mathrm{y + lowbit(y) = x}\))

技术图片

那么我们通过改变一个值的大小来代表改变一段区间的(子树)所有数的大小。

比如点\(x\)对应(可以理解为一种代表)\([x - \mathrm{lowbit(x)} + 1, \ x]\)一段区间内的数。对这一段区间进行区间加的时候只需要修改x就行了。

那么对于序列\(\{a_i\}\) ,我们需要维护一个\(\{c_i\}\),定义如下

\[c_n\ =\ \sum_{i\ =\ n - \mathrm{lowbit(n) + 1}}^{n} a_i \]

\(c_n\)为所有子树内\(a_i\)的和。

如何使用树状数组

我们需要掌握修改、查询等操作

修改

我们进行修改操作的时候,可以选择是进行前缀修改还是后缀修改。

本质上都是利用了树状数组的性质,所以按照习惯来吧。

修改操作之前说过只需要修改某些特定的点就行了。

比如修改\([1,\ 9]\) 一段区间。

我们可以先\([1,\ N]\)修改,再\([9\ +\ 1, N]\)逆修改。

修改之后的树形态:技术图片

我们只修改了少数点,而且个数是\(\mathrm{O(log_2 n)}\)级别的,数量越大优势越明显。

\(\mathrm{Code:}\)

inline void inc(int x, int v) {for(; x <= MAX
N; x += lowbit(x)) c[x] += v;}

查询

那么有人就要问了,这个修改后的形态这么鬼畜,改怎么获得正确的查询信息呢?

我们发现\(lowbit\)的另一个快乐的性质:

\[y = y - lowbit(y)操作能使y到达上一棵同级(或上级)子树的树根 \]

比如上面那个栗子,无论9还是10都可以通过这个运算到达8.

那不是很爽么。单次修改中某个点若在范围内,则其同级子树必有一个被修改。直接通过\(lowbit\)累计即可。

容易发现

\[a_n = \sum_{i = n;\ i;\ i -=lowbit(i)}c_i \]

我们可以在\(O(log_2n)\)时间内完成查询。

\(\mathrm{Code:}\)

inline int ask(int x) {
    int sum = 0;
    for (; x; x -= lowbit(x)) sum += c[x];
    return sum;
}

一些拓展

比如前缀(后缀)最值,可以通过树状数组快速维护。

代码实现十分简单,只需要将\("+"\) \("-"\)改成\("max"\) \("min"\)

Just like:

inline void inc(int x, int v) {for (; x <= MAXN; x += lowbit(x)) c[x] = max(c[x], v);} 
//操作这里min也是一样的
inline int ask(int x) {
    int maxn = -1;
    for (; x; x -= lowbit(x)) maxn = max(maxn, c[x]); //注释同上
    return maxn;
}

二维操作&各种基本功能实现详见Pαrsnip的blog

还有比如什么树状数组上二分(倍增)、一些小Trick、简单代替线段树等黑科技。

尽在cz的树状数组黑科技讲义.

树状数组应用

一维/二维/(你想写高维可能也没什么)区间/单点修改,区间/单点查询的 区间四则运算/前缀(后缀)最值

当然更多的应用无处不在与各个地方。

题目基本是让你求一个序列中所有连续子序列的某个贡献值。


<后记>

为树状数组专题备用基础讲解的blog。

树状数组预备

标签:结构   span   查询   使用   二维   级别   code   ali   div   

原文地址:https://www.cnblogs.com/zqytcl/p/13098913.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!