树状数组的区间修改+查询

首先看树状数组是用来求前缀和比较方便的一种数据结构
sum[i] = Sigma a[i] =Sum(bit[x])
而区间修改也不难实现
就是引入一个差分数组del
del[i]表示对i~n的修改
这样的话也就是最del[i]求前缀和 就能得到i~n的所有修改了
因为i前的每一个元素的修改都是对后面所有元素的修改
所以当我们统计i~n的修改的时候 我们需要把前面的修改项都累加起来才行
这里就可以用树状数组统计一下前缀和

也就是当我们对区间s~e修改的时候 用del[s]+add 表示对i~n的修改
但是e之后不要修改 所以我们再执行 del[e+1]-add的操作
这样当我们进行区间修改 单点查询的时候 就用原始a[i]+sum(del[i])【从1-i的信息累加之和】即可表示

当我们需要求区间查询s~e的时候
我们先来看如何求1~i的和
sigma(i) = a[1] + del[1]+ a[2]+ del[1] + del[2] +……+a[i]+del[1]+del[2]+……+del[i]
= a[1]+a[2]+……+a[i] + del[1](i)+del[2] (i-1) +……+del[i] *1
= a[1]+a[2]+……+a[i] + del[1](i-1+1)+del[2] (i-2+1) +……+del[i] *(i-i+1)
= Sigma(a[xi]) + Sigma(del[ xi ]*( i -xi +1 ) )
= Sigma(a[xi]) + Sigma(del[ xi ])* ( i +1 ) ) -Sigma(del[xi] * xi )

可知 第一个Sigma 是静态的 可以用前缀和预处理出来
第二个Sigma: i+1也是不变的 用树状数组统计差分变化 然后求和即可
第三个Sigma: 需要另开辟一个树状数组idel 统计i*del[xi]的前缀和
然后当我们修改区间段的时候 对差分数组del[s]+add 需要同时对 idel[s] + add * s因为最后一个sigma就是表示要把对差分数组的每次修改乘上修改的位置/【下标】
然后在求累加和的时候 自然而然的就把sigma(del[xi]*xi)求出来了

经典例题:
POJ- 3468 A Simple Problem with Integers 线段树区间修改+查询 | 树状数组的区间修改+查询
题意就是经典的两种操作
Q s e 表示查询s到e区间内的总和
C s e add 表示把s到e都加上一个add
区间内的元素大小1e9
区间长度和查询次数1e5

分析:
这道题虽然是课裸的线段树区间修改+查询+延迟标记
但是繁杂的代码不如用实现简单的树状数组来做
code:

#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll a[maxn],Sum[maxn],del[maxn],idel[maxn];
// 树状数组 区间更新 区间修改
//维护三个树状数组 1 原始树状数组 
//                 2 差分树状数组 del
//                 3 i*del[i] 的树状数组
int n,m;
void update(ll a[],int x,int add){
    while(x<=n){
        a[x]+=add;
        x+=x&(-x);
    }
}
ll sum(ll a[],int x){
    ll s1=0;
    int tmp = x;
    while(tmp>0){
        s1+=a[tmp];
        tmp-=tmp&(-tmp);
    }
    return s1;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        Sum[i] = Sum[i-1]+a[i];
    }
    while(m--){
        string o;
        int s,e,add;
        cin>>o;
        if(o[0]=='Q'){
            cin>>s>>e;
            ll ss = Sum[s-1] + s*sum(del,s-1) - sum(idel,s-1); 
            ll se = Sum[e] + (e+1)*sum(del,e) - sum(idel,e);
            cout<else{
            cin>>s>>e>>add;
            //维护区间差分数组 求和时需要用 (x+1)*Sigma(del[x]) 
            update(del,s,add);
            update(del,e+1,-add);
            //维护需要减去的 i*del[i]数组 求和时需要减去 Sigma(i*del[i]) 
            update(idel,s,s*add);
            update(idel,e+1,(e+1)*(-add));
        }
    } 
    return 0;
} 

你可能感兴趣的:(数据结构,树状数组)