树状数组的应用(区间修改+区间查询)

来源:互联网 发布:发泥推荐 知乎 编辑:程序博客网 时间:2024/06/01 08:43

树状数组之————区间修改+区间查询

树状数组的工作是 对一组数据进行快速修改查询操作

最基本的功能是 单点修改+区间查询。

然后厉害的是 区间修改+单点查询(用数组del[i]表示原数组a[i]-a[i-1]的值)

更厉害的来了。。。

区间修改+区间查询

用数组del[i]记录原数组a[i]与前一项和,即del[i]=a[i]-a[i-1]

求数组a的前n项和就是:

S(n) = a[1]+a[2]+...a[n]

= del[1] + (del[1]+del[2]) + .... + (del[1]+del[2]+....del[n])

= n*del[1] + (n-1)*del[2] + .... + 1*del[n]

= (n+1 -1)*del[1] + (n+1 -2)*del[2] + .... + (n+1 -n)*del[n]

= (n+1)*(del[1]+del[2]+ ... +del[n]) - (1*del[1] + 2*del[2] + ... + n*del[n])

= (n+1)*sum(del[i], 1<=i<=n) - sum(i*del[i], 1<=i<=n)


其实只需要求出(n+1)*sum(del[i]) ,和 sum(i*del[i], 1<=i<=n),相减即可

所以就用两个树状数组共同维护

用数组c1[i]维护del[i]

用数组c2[i]维护i*del[i]

对数组进行操作时,用时更新这两个数组即可。查询时,用上面推导的公式

对于那两个查询和修改函数不变,依然沿用最老套的模板


代码实现详见例题:1082 线段树练习 3

【代码】:

#include<stdio.h>  #include<iostream>  using namespace std;  typedef long long ll;  ll a[202020];  ll del[202020];  ll c1[202020];//维护del  ll c2[202020];//维护x*del[x]  ll n;  void add(ll *c,ll k,ll num)  {      for(;k<=n;k+=k&-k)          c[k]+=num;  }  ll read(ll *c,ll k)//查询区间1~k  {      ll sum=0;      for(;k;k-=k&-k)          sum+=c[k];      return sum;  }ll sum(ll k){return (k+1)*read(c1,k)-read(c2,k);  }int main()  {      cin>>n;      for(ll i=1;i<=n;i++){          cin>>a[i];          del[i]=a[i]-a[i-1];          add(c1,i,del[i]);          add(c2,i,del[i]*i);      }      ll Q;cin>>Q;      while(Q--)      {          ll oper;cin>>oper;          if(oper==1)          {              ll a,b,num;              cin>>a>>b>>num;              add(c1,a,num);              add(c1,b+1,-num);              add(c2,a,num*a);              add(c2,b+1,-num*(b+1));          }          else          {              ll a,b;cin>>a>>b;              ll sumr=sum(b);              ll suml=sum(a-1);            cout<<sumr-suml<<endl;          }      }  }