树状数组(单点修改+区间求和)

主要是清楚lowbit的用法
树状数组模板题

int lowbit(int x){//返回x二进制中首次出现1的位置所代表的值,如(当x=6时返回2)
    return x&(-x);//利用原码和补码的关系
}

树状数组的形成示意图:
在这里插入图片描述
图中我们可以发现2的二进制是010所以它的lowbit值也为2即表明c2可以将A2与A1两个元素联系起来即c2为A【1,2】的区间和,同理c4可以联系4个(自己与前个),c6可以联系两个(因为lowbit(110)==2)即自己和A5.
由此可以建立树状数组:

void build(int t){
    for(int i=1;i<=t;i++){
        for(int j=i;j>=i-lowbit(i)+1;j--){
            ans[i]+=A[j];
        }
    }
}

树状数组的单点修该在对应点本修改时也要修改其祖先,如改节点1时,c2,c4,c8等区间和也要对应改变,通过观察下标可以发现lowbit(1)+lowbit(1)=2,lowbit(2)+lowbit(2)=4,也就是说节点的父亲节点的下标等于子节点下标加子节点下标的lowbit值,从而可以得出单点修改代码:

void xiugai(int a,int b){
    while(a<=n){
        ans[a]+=b;
        a+=lowbit(a);
    }
}

树状数组区间求和也是通过观察其下标规律,如:若求A【1,7】的值,即求c7+c6+c4,其中7(111),6(110),4(100),可以发现:
6=7-lowbit(7),4=6-lowbit(6),直到4-lowbit(4)=0终止:
由此可以得出区间求和代码:

ll sum(int a){
    ll t=0;
    while(a>=1){
        t+=ans[a];
        a-=lowbit(a);
    }
    return t;
}

最后附完整代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll A[500005],ans[500005]; ll n,m;
int lowbit(int x){
    return x&(-x);
}
void build(int t){
    for(int i=1;i<=t;i++){
        for(int j=i;j>=i-lowbit(i)+1;j--){
            ans[i]+=A[j];
        }
    }
}
ll sum(int a){
    ll t=0;
    while(a>=1){
        t+=ans[a];
        a-=lowbit(a);
    }
    return t;
}
void xiugai(int a,int b){
    while(a<=n){
        ans[a]+=b;
        a+=lowbit(a);
    }
}
void chaxun(int x){
    if(x==1){
        int a,b; cin>>a>>b;
        xiugai(a,b);
    }
    if(x==2){
        int a,b; cin>>a>>b;
        ll t=sum(b)-sum(a-1);//利用前缀和思想求区间和
        cout<<t<<endl;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>A[i];
    }
    build(n);
    for(int i=0;i<m;i++){
        int e; cin>>e;
        chaxun(e);
    }
}

发布了24 篇原创文章 · 获赞 2 · 访问量 452

猜你喜欢

转载自blog.csdn.net/chineseherofeng/article/details/104910806
今日推荐