天天看点

树状数组维护区间和和区间修改作用实现方法代码

作用

可以用树状数组在(n*logn)内,虽然线段树也能,但是树状数组的代码,空间都要比它优越得多.

实现方法

首先我们可以用差分的方法使区间修改可以在log的复杂度完成,但重点在于区间和的查询.

我们知道,此时num[i]=a[1]+a[2]+……a[i],可以利用树状数组快速求出.

而区间和则是(a[1]+a[2]+……a[i])+(a[1]+a[2]+……a[i-1])……..+(a[1]+a[2])+a[1].

也就是i*(a[1]+a[2]+……a[i])-(a[1] * 0+a[2] * 1……a[i] *(i-1)).

前半部分可以用树状数组维护,后面可以再建一个树状数组维护(i-1)*a[i]的前缀和.

就可以用两个树状数组维护区间和和区间修改

代码

以洛谷 P3372 【模板】线段树 1为例.

#include<iostream>
#include<cstdio>
#define lb(x) (x&(-x))
#define ll long long
#define N 100100
using namespace std;

ll n,m,s1[N],s2[N],num[N];

inline void add(ll u,ll v,ll w)
{
    for(;u<=n;u+=lb(u))
    {
        s1[u]+=v;
        s2[u]+=w;
    }
}

inline ll ask(ll u)
{
    ll res=,i;
    for(i=u;i;i-=lb(i))
    {
        res+=s1[i];
    }
    res*=u;
    for(i=u;i;i-=lb(i))
    {
        res-=s2[i];
    }
    return res;
}

int main()
{
    ll i,j,o,p=,q;
    cin>>n>>m;
    for(i=;i<=n;i++)
    {
        scanf("%lld",&num[i]);
        q=num[i];
        num[i]-=p;
        p=q;
    }
    for(i=;i<=n;i++)
    {
        add(i,num[i],num[i]*(i-));
    }
    for(i=;i<=m;i++)
    {
        scanf("%lld",&o);
        if(o==)
        {
            scanf("%lld%lld%lld",&p,&q,&o);
            add(p,o,(p-)*o);
            add(q+,-o,-q*o);
        }
        else
        {
            scanf("%lld%lld",&p,&q);
            printf("%lld\n",ask(q)-ask(p-));
        }
    }
}
           

继续阅读