最近本蒟蒻学了树状数组,很喜欢这个数据结构因为这代码确实短

好了废话不多说直接正题。

树状数组是一种新颖的数据结构,这个数据结构1997年才发明,很年轻的一个数据结构。

这里说一个位运算lowbit,lowbit(x)表示取出x的最后一位1的数值。lowbit(x)=x&-x

先来一张手绘图形成一个直观的认识:(纯手绘勿喷)

lua给一个数组加一个元素 给数组加值_ci

图中a数组就是底层的那个12345678,树状数组就是飘在空中的12345678,我这样描述是不是有点那啥

好了那这个东西是干啥的呢?

当我们要求出一个不断变化的连续一段区间的和时就会用到它。

所以引例:

问题描述

假设有一列数{Ai}(1≤i≤n),支持如下两种操作:
将Ak的值加D。(k, D是输入的数)
输出As+As+1+…+At。(s, t都是输入的数,S≤T)

输入格式

第一行一个整数n,
第二行为n个整数,表示{Ai}的初始值≤10000。
第三行为一个整数m,表示操作数
下接m行,每行描述一个操作,有如下两种情况:
ADD k d (表示将Ak加d,1<=k<=n,d为数,d的绝对值不超过10000)
SUM s t (表示输出As+…+At)

输出格式

对于每一个SUM提问,输出结果

树状数组的模板题QAQ。

前缀和什么的直接TLE,可以使用树状数组。

当然这道题也可以线段树,但是常数比树状数组高很多。

把图再放一遍:

lua给一个数组加一个元素 给数组加值_数据结构_02

c数组每个元素向下连接的线段,代表它的值是这些线段的另一端元素的和。

例如图中 lua给一个数组加一个元素 给数组加值_数据结构_03

那么这个东西怎么求和呢?

观察每个编号的二进制表示:

lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_04
lua给一个数组加一个元素 给数组加值_树状数组_05
lua给一个数组加一个元素 给数组加值_数据结构_06
lua给一个数组加一个元素 给数组加值_ci_07
lua给一个数组加一个元素 给数组加值_ci_08
lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_09
lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_10
lua给一个数组加一个元素 给数组加值_树状数组_11

可以观察到是很有规律的。

显然,当lua给一个数组加一个元素 给数组加值_树状数组_12时,lua给一个数组加一个元素 给数组加值_数据结构_13

再试着找规律,最后一个式子好像给了我们什么启发。

lua给一个数组加一个元素 给数组加值_ci_14lua给一个数组加一个元素 给数组加值_数据结构_15

所以代码不难写出:

for (int i = x; i > 0; i -= lowbit(i)) c[x] += c[i];

这段代码能求出 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_16

好了还是没有说怎么求和啊……

观察可知,lua给一个数组加一个元素 给数组加值_树状数组_17

所以 lua给一个数组加一个元素 给数组加值_数据结构_18……lua给一个数组加一个元素 给数组加值_数据结构_15

代码也不难写出了:

inline int GetSum(int x)//求a[1]~a[x]的和
{
	int sum = 0;
	for (int i = x; i > 0; i -= lowbit(i)) sum += c[i];
	return sum;
}

上面的所有代码不理解也要背会,只要背会了还是能解决树状数组的题目。

SUM操作我们就完成了,那么怎么完成ADD操作呢?

其实我们根本不用理 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_20 数组,直接更新树状数组 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_21

根据上面推出 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_16 的式子或观察每个 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_23 会影响到的其他 lua给一个数组加一个元素 给数组加值_lua给一个数组加一个元素_23,代码不难写出:

inline void modify(int x, int d)//将c[x]加上d并更新树状数组
{
	for (int i = x; i <= n;  i += lowbit(i)) c[i] += d;
}

同样,上面的代码不理解也要背会。但还是希望大家勤于观察找规律,理解这些代码背后的原理。

这样,不难写出本题的完整程序了:

#include <cstdio>
#define lowbit(x) (x & -x)

int c[100005], n;

inline void modify (int x, int d)
{
    for (int i = x; i <= n; i += lowbit(i)) c[i] += d;
}

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

inline int Getchar()
{
    char ch;
    while ((ch = getchar()) == '\n' || ch == ' ');
    return ch;
}//这题输入是真的坑,我也不知道怎么scanf就死了

int main()
{
    int q, t, a, b;
    char ch;
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
    {
        scanf("%d", &t);//本题的细节,输入a[i]可以理解为将c[i]加上a[i]
        modify(i, t);
    }
    scanf("%d", &q);
    getchar();
    while (q --)
    {
        ch = Getchar();
        getchar();//输入恶心死了,只需要读取首字母就能判断什么操作
        getchar();
        scanf("%d%d", &a, &b);
        if (ch == 'S')
            printf("%d\n", GetSum(b) - GetSum(a - 1));
        else
            modify(a, b);
    }
}

好了树状数组的初步讲解就到这里了QAQ。

End