浅谈——树状数组

树状数组

1. 建树

C1 = A1
C2 = C1 + A2 = A1 + A2
C3 = A3
C4 = C2 + C3 + A4 = A1 + A2 + A3 + A4
C5 = A5
C6 = C5 + A6 = A5 + A6
C7 = A7
C8 = C4 + C6 + C7 + A8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

把对a数组的子序列问题拆分放到C数组,从而实现(longn)求解

建树

int lowbit(x){return x&(-x);}

极致简短!!!现在我们来理解一下这行代码:

我们知道,对于一个数的负数就等于对这个数取反+1

以二进制数11010为例:11010的补码为00101,加1后为00110,两者相与便是最低位的1

其实很好理解,补码和原码必然相反,所以原码有0的部位补码全是1,补码再+1之后由于进位那么最末尾的1和原码

最右边的1一定是同一个位置(当遇到第一个1的时候补码此位为0,由于前面会进一位,所以此位会变为1)
不理解就死记硬背

所以我们只需要进行a&(-a)就可以取出最低位的1了

会了lowbit,我们就可以进行建树了

#include<bits/stdc++.h>
using namespace  std;
int a[11],c[11];
int lowbit(int x){return x&(-x);}取出最后一位的1
void add(int k,int v){for(int i=k;i<=10;i+=lowbit(v))c[i]+=v;}更新数组
int main()
{
   for(int i=1;i<=10;i++)cin>>a[i],add(i,a[i]);
   for(int i=1;i<=10;i++)cout<<c[i]<<endl;
   return 0;	
   5 5+=lowbit(5) 101 2^0=1 5+1=6
   6 6+=lowbit(6) 110 6+2=8;
   
   }

--------------------------------------------------------

单点修改&&区间和

单点更新:
继续看开始给出的图

此时如果我们要更改A[1]

则有以下需要进行同步更新

 

1(001)        C[1]+=A[1]

lowbit(1)=001 1+lowbit(1)=2(010)     C[2]+=A[1]

lowbit(2)=010 2+lowbit(2)=4(100)     C[4]+=A[1]

lowbit(4)=100 4+lowbit(4)=8(1000)   C[8]+=A[1]

换成代码就是:

void find(int x,int y,int n){
    for(int i=x;i<=n;i+=lowbit(i))    //x为更新的位置,y为更新后的数,n为数组最大值
        c[i] += y;
从1--x的前缀和
int getsum(int x){
    int ans = 0;
    for(int i=x;i;i-=lowbit(i))
        ans += c[i];
    return ans;

--------------------------------------------------------

例题

题目描述

给出n个数(1 < = n < = 100000 ),并且初始化所有数字都为0.接下来m次操作,( 1<= m < = 100000 )
操作有以下两种:
1: C X K 把第X个数的值增加K(K可正可负)a[X]:=a[X]+K
2: P X Y 就是询问 第X个数至 第Y个数 的所有数的和。 writeln( a[X] + a[X+1] + a[X+2] + …+ a[Y] )

Input
5 3
C 2 3
C 4 5
P 1 5

Output
8

#include<bits/stdc++.h>
#define maxn 150000
using namespace  std;
long long a[maxn],c[maxn],n,m;
int lowbit(int x){return x&(-x);}
void add(int k,int v){for(int i=k;i<=n;i+=lowbit(i))c[i]+=v;}
int getsum(int k){int ans=0;for(int i=k;i>=1;i-=lowbit(i))ans+=c[i];return ans;}
int main()
{
   cin>>n>>m;
   for(int i=1;i<=m;i++)
   {
    int x,k;
   	char c;cin>>c>>x>>k;//输入 
   	if(c=='C')add(x,k);//如果是加,那么add 
	else  cout<<getsum(k)-getsum(x-1)<<endl;//求和 
   }
   return 0;	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值