树状数组学习摘要

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

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

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

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

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

在这里插入图片描述

图中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,可以使用树状数组。

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

把图再放一遍:

在这里插入图片描述

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

例如图中 c 1 = a 1 , c 2 = c 1 + a 2 , c 4 = c 2 + c 3 + a 4 c_1=a_1,c_2=c_1+a_2,c_4=c_2+c_3+a_4 c1=a1,c2=c1+a2,c4=c2+c3+a4

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

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

c 001 = a 001 c_{001}=a_{001} c001=a001
c 010 = c 001 + a 010 c_{010}=c_{001}+a_{010} c010=c001+a010
c 011 = a 011 c_{011}=a_{011} c011=a011
c 100 = c 010 + c 011 + a 100 c_{100}=c_{010}+c_{011}+a_{100} c100=c010+c011+a100
c 101 = a 101 c_{101}=a_{101} c101=a101
c 110 = c 101 + a 110 c_{110}=c_{101}+a_{110} c110=c101+a110
c 111 = a 111 c_{111}=a_{111} c111=a111
c 1000 = c 100 + c 110 + c 111 + a 1000 c_{1000}=c_{100}+c_{110}+c_{111}+a_{1000} c1000=c100+c110+c111+a1000

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

显然,当 c i = a i c_i=a_i ci=ai时, l o w b i t ( i ) = 1 lowbit(i)=1 lowbit(i)=1

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

c i = c i − l o w b i t ( i ) + c i − l o w b i t ( i ) − l o w b i t ( i − l o w b i t ( i ) ) + c_i=c_{i-lowbit(i)}+c_{i-lowbit(i)-lowbit(i-lowbit(i))}+ ci=cilowbit(i)+cilowbit(i)lowbit(ilowbit(i))+ c 0 c_0 c0

所以代码不难写出:

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

这段代码能求出 c x c_x cx 的值。

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

观察可知, c x = ∑ i = x − l o w b i t ( x ) + 1 x a c_x=\sum^x_{i=x - lowbit(x)+1}a cx=i=xlowbit(x)+1xa

所以 ∑ i = 1 r a = c r + c r − l o w b i t ( r ) + \sum^r_{i=1}a=c_r+c_{r-lowbit(r)}+ i=1ra=cr+crlowbit(r)+…… c 0 c_0 c0

代码也不难写出了:

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操作呢?

其实我们根本不用理 a a a 数组,直接更新树状数组 c c c 数组即可。

根据上面推出 c x c_x cx 的式子或观察每个 c i c_i ci 会影响到的其他 c i c_i ci,代码不难写出:

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值