树状数组

详细解析

树状数组可以解决查询前缀和/区间和问题。

模板(单点修改)

int lowbit(int i)
{
    return i & -i;//或者是return i-(i&(i-1));表示求数组下标二进制的非0最低位所表示的值
}
void add(int i,int val)//更新单节点的值
{
    while(i<=n){
        a[i]+=val;
        i+=lowbit(i);//由叶子节点向上更新a数组,从左往右更新
    }
}
int sum(int i)//求和节点的值
{
    int ret=0;
    while(i>0){
        ret+=a[i];//从右往左区间求和
        i-=lowbit(i);
    }
    return ret;
}

区间修改:

区间修改的理论依据是差分数组,维护的数组有两个,一个是差分数组c1。还有个是c2是pos*value;

模板 :

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=5E5+7;
ll d[N];
ll arr[N];
ll n,m;
ll c1[N],c2[N];
ll lowbit(ll x){
    return x&(-x);
}
void add(ll x,ll value){
    ll x1=x;
    while(x<=n){
        c1[x]+=value;
        c2[x]+=x1*value;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll res=0;
    ll x1=x;
    while(x){
        res+=(x1+1)*c1[x]-c2[x];
        x-=lowbit(x);
    }
    return res;
}
ll query(ll x,ll y){
    return sum(y)-sum(x-1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(ll i=1;i<=n;i++) scanf("%lld",&arr[i]);
    arr[0]=0;
    for(ll i=1;i<=n;i++){
         add(i,arr[i]-arr[i-1]);
    }
    for(ll i=1;i<=m;i++){
        int k;
        scanf("%d",&k);
        if(k==1){
            ll x,y,value;
            scanf("%lld%lld%lld",&x,&y,&value);
            add(x,value);
            add(y+1,-value);
        }
        else {
            ll pos;
            scanf("%lld",&pos);
            printf("%lld
",query(pos,pos));
        }
    }
    return 0;
}

lowbit返回的是i的二进制最右端1所对应的值。

 区间和[x,y]:可以通过sum(y)-sum(x-1)来实现。

注:树状数组可以解决的一般线段树都可以解决,树状数组只能是区间加减单点加减。

原文地址:https://www.cnblogs.com/Accepting/p/12397231.html