树状数组O(n)建树

题目来源
以这道题为例子
在这里插入图片描述
在这里插入图片描述


首先我们知道树状数组是这样的;在这里插入图片描述
图中的C数组就是我们的树状数组;

暴力建树

首先我们可以通过单点修改的方式,暴力建树,但是这种是 O ( n l o g n ) O(nlogn) O(nlogn)的,如下;

Code

void add(int u,int v){
    for(int x = u; x<=n; x+=lowbit(x)){
        tr[x] += v;
    }
}
//暴力建树
void build(){
	for(int i=1;i<=n;++i){
        add(i,b[i]);
    }	
}

O(n)方法一

有一个规律;
C [ i ] = a [ x − l o w b i t ( x ) + 1 , x ] C[i]=a[x-lowbit(x)+1,x] C[i]=a[xlowbit(x)+1,x]

对于原数组 a a a的每个端点 R R R,其树状数组覆盖的长度是 l o w b i t ( R ) lowbit(R) lowbit(R)

这样我们就可以通过前缀和来实现 O ( n ) O(n) O(n)的初始化

Code

void build(){
    for(int i=1;i<=n;++i){
        //tr[i] = [x-lowbit(x)+1,x]
        //s是前缀和数组
        tr[i] = s[i] - s[i-lowbit(i)];
    }
}

O(n)方式二

在观察一下那张图,我们可以发现,对于每个节点的值,都是通过其儿子得到的;

而暴力版本之所以是 O ( n l o g n ) O(nlogn) O(nlogn)是因为重复计算了一些点,而我们如果按顺序枚举每个点且不重复,那么复杂度必然是 O ( n ) O(n) O(n)

Code

void build(){
    for(int i=1;i<=n;++i){
        tr[i] += b[i];
        int fa = i + lowbit(i);
        if(fa <= n){
            tr[fa] += tr[i];
        }
    }
}

例题完整代码

#include <iostream>

using namespace std;

const int N = 1e5+10;

typedef long long ll;

int n,m,a[N],b[N],s[N];

ll tr[N];

int lowbit(int x){
    return x & -x;
}
ll query(int x){
    ll ret = 0;
    for(int i=x;i;i-=lowbit(i)){
        ret += tr[i];    
    }
    return ret;
}
void add(int u,int v){
    for(int x = u; x<=n; x+=lowbit(x)){
        tr[x] += v;
    }
}
void build(){
    for(int i=1;i<=n;++i){
        tr[i] += b[i];
        int fa = i + lowbit(i);
        if(fa <= n){
            tr[fa] += tr[i];
        }
    }
}
int main(){
    cin >> n >> m;
    for(int i=1;i<=n;++i){
        cin >> a[i];
        b[i] = a[i] - a[i-1];
        s[i] = b[i] + s[i-1];   
    }
    build();
    char ch;
    while(m--){
        cin >> ch;
        if(ch == 'C'){
            int l,r,v;
            cin >> l >> r >> v;
            add(l,v);
            add(r+1,-v);
        }else{
            int x;
            cin >> x;
            cout << query(x) << '\n';
        }
    }
    return 0;
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值