树状数组(区间,单点间操作)

假设一维数组为A[i]( i=1 ,2,…n ),则与它对应的树状数组C[i](i=1 ,2,…n)是这样定义的:

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

……

C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

……
这里写图片描述
一个有趣的性质
第C(x)管辖的区域为:2^k(k为二进制末尾的0的个数)
比如:
C(8) 8的二进制为1000 所以 k=3 2^3=8

int lowbit (int x)//计算C(x)展开的项数
{
   return x&-x;
}

X即为数组的下标
意义:计算2^k的值,即C(x)管辖的区域
第二个函数:

int add (int x,int b)
{
    while(x<=MAXN)
    {
        Tree(x)+=b;
        x+=lowbit(x);
    }
}

解释:

1:b是增量在树的结构中,每次增加b,即为修改了多少,是一个变量。修改了A(x)的值,会一直追溯到最终的父节点。

比如:A(2)增加了b,则该循环使其父节点依次增加b个单位。
C(2) C(4) C(8) 依次增加了b个单位。
2:x是数组的下标。
这里写图片描述

第三个函数:

int sum (int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=tree[x];
        x-=lowbit(x);
    }
    return ret;
}

解释:

1:求出范围a-b的和

Sum(b)-sum(a)即为所求。

求数列A[]的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。

如:Sum(1)=C[1]=A[1];

  Sum(2)=C[2]=A[1]+A[2];

  Sum(3)=C[3]+C[2]=A[1]+A[2]+A[3];

  Sum(4)=C[4]=A[1]+A[2]+A[3]+A[4];

  Sum(5)=C[5]+C[4];

  Sum(6)=C[6]+C[4];

  Sum(7)=C[7]+C[6]+C[4];

  Sum(8)=C[8];

这里写图片描述
这里写图片描述

一:区间修改,单点查询

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1556
**

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include <cstdlib>
#include <cstdio>
#include <map>
#define lson l,m,i<<1
#define rson m+1,r,i<<1|1
#define lowbit(x) (x&(-x))
using namespace std;

const int  MAXN = 100000;
const int  LEN = MAXN + 100;
int tree[LEN];
int N;
void Add(int x,int p)//
{
    while(x<=N)
    {
        tree[x] += p;
        x += lowbit(x);
    }
}
int Query(int x)
{
    int sum = 0;
    while(x)
    {
        sum += tree[x];
        x -= lowbit(x);
    }
    return sum;
}
int main()
{
    while(cin>>N&&N)
    {
        memset(tree,0,sizeof(tree));
        for(int i = 1; i <= N; i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            Add(a,1);
            Add(b+1,-1);
        }
        for(int i = 1; i <= N; i++)
        {
            if(i>1)
                printf(" ");
            printf("%d",Query(i));
        }
        cout<<endl;
    }
    return 0;
}

**

二 :单点修改,区间查询

**

int low_bit(int x)
{
    return x&(-x);
}
void add(int i,int c)
{
    while(i<=n)   //n是一共有多少点
    {
    a[i]+=c;
    i+=low_bit(i);
    } 
}
int q(int i)
{
    int ans=0;
    while(i>0)
    {
    ans+=a[i];
    i-=low_bit(i);
    }   
    return ans;   
} 
int main()
{
    add(i,c);   //  i是对那个点增加   c 是要增加的多少   
    //若查询  l--r  区间的和
    ans=q(r)-q(l-1);
}

**

三 :区间修改,区间查询

**
略……………

部分转载自:https://blog.csdn.net/qq_24653023/article/details/46991325

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值