树状数组

这几天一直学习树状数组,可是还是一知半解。

 如果给定一个数组,要你求里面所有数的和,一般都会想到累加,但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当改掉数组中的某一个元素之后,仍然让你求数组中某段元素的和,就显得麻烦 了,所以我们就要用到树状数组,它到 时间复杂度为O(lgn),相比之下就快的多,。下面就说一下什么是树状数组:

树状数组主要用到一个累加的思想,下面来看一下树状数组图,方便理解:




根据上图可知:

c1=a1
c2=a1+a2
c3=a3
c4=a1+a2+a3+a4
c5=a5
c6=a5+a6
c7=a7
c8=a1+a2+a3+a4+a5+a6+a7+a8
c9=a9
等等
分析上面的几组式子可知,当i为奇数时,ci=ai;
当i为偶数时,就要看i的因子中最多有二的多少次幂,例如,6的因子中有二的一次幂,等于2:
所以c6=a5+a6(由六向前数俩个数的和),4的因子中有二的俩次幂,,等于四,所以c4=a1+a2+a3+a4(由四向前数四个数的和);
(1)有公式cn=a(n-a^k+1)+.....an(其中k为n的二进制表示中从右往左数的0的个数)
下面是求a^k方法:
int low(int n)
{
	return n&(-n);
}
low()的返回值就是2^k次方的值。
求出2^k之后,数组c的值就都可以求出来了。接下来我们要求数组中所有元素的和。
(2)求数组的和的算法如下:
(a)首先,令sum=0,执行下一步;
(b)判断,如果n>0的话,就令sun=sum+cn,继续执行下一步,否则,终止算法,返回sum的值。
(c)n=n-low(n)(将n的二进制表示的最后一个零删掉),返回第二步继续执行。
代码实现:
int sum(int n)
{
	int sum=0;
	while(n>0)
	{
		sum=sum+c[n];
		n=n-low(n);
	}
	return sum;
}
(3)当数组中的元素有改变时,树状数组就可以发挥它的优势了,算法如下:(数组改变时,是给某一个结点i加上x)
(a)当i<=n时,执行下一步,否则的话,算法接受;

 
(b)c[i]=c[i]+x;i=i+low(i)(在i的二进制表示的最后加零),返回第一步。
代码实现:
<pre name="code" class="csharp">void change(int i,int x)
{
	while(i<=n)
	{
		c[i]=c[i]+x;
		i=i+low(i);
	}
}
 
 
 

这是一道模板题:请直接分析代码即可,注意这种题目cin和cout容易TLE

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
using namespace std;
 
int N;
int a[50010];
int c[50010];
 
int lowbit(int x)
{
    return x&-x;
}
 
void update(int i , int num)
{
    while(i <=  N)
    {
        c[i] += num;
        i += lowbit(i);
    }
}
 
int getsum(int x)
{
    int sum = 0;
 
    while(x > 0)
    {
        sum += c[x];
        x -= lowbit(x);
    }
 
    return sum;
}
 
int main()
{
    int t,z;
    scanf("%d",&t);
    z = 1;
    while(t--)
    {
        scanf("%d",&N);
 
        memset(c,0,sizeof(c));
 
        for(int i = 1; i <= N; i++)
        {
            int m;
          
           scanf("%d",&m);
            update(i,m);
        }
        printf("Case %d:\n",z++);
        char str[10];
        while(scanf("%s",str) && str[0] != 'E')
        {
 
            if(str[0] == 'Q')
            {
                int i,j;
                 scanf("%d%d",&i,&j);
               printf("%d\n",getsum(j) - getsum(i-1));
 
            }
            else if(str[0] == 'A')
            {
                int x,y;
                scanf("%d%d",&x,&y);
                update(x,y);
            }
            else if(str[0] == 'S')
            {
                int p,q;
                scanf("%d%d",&p,&q);
                update(p,-q);
            }
 
        }
 
    }
    return 0;
}








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值