树 状 数 组 : 区 间 修 改 , 区 间 查 询 树状数组 :区间修改,区间查询 树状数组:区间修改,区间查询
一、树状数组是什么?
新手请参考 — — — — 》 》 ————》》 ————》》
二、区间修改与区间查询
凡是涉及到区间修改,就必须用到 差 分 差分 差分
求前缀和:
-
a p a_p ap 的前缀和: ∑ i = 1 p a [ i ] = ∑ i = 1 p ∑ j = 1 i d [ j ] \sum_{i=1}^{p}a[i]=\sum_{i=1}^{p}\sum_{j=1}^{i}d[j] ∑i=1pa[i]=∑i=1p∑j=1id[j]
-
在 ∑ i = 1 p ∑ j = 1 i d [ j ] \sum_{i=1}^{p}\sum_{j=1}^{i}d[j] ∑i=1p∑j=1id[j]中, d 1 d_1 d1 被用了 p p p 次, d 2 d_2 d2 被用了 p − 1 p-1 p−1 次……那么我们
可以写出:
a p a_p ap 的前缀和: ∑ i = 1 p ∑ j = 1 i d [ j ] = ∑ i = 1 p d [ i ] × ( p − i + 1 ) = ( p + 1 ) × ∑ i = 1 p d [ i ] − ∑ i = 1 p d [ i ] × i \sum_{i=1}^{p}\sum_{j=1}^{i}d[j]=\sum_{i=1}^{p}d[i]\times(p-i+1)= \\ \\ \\ \\(p+1)\times\sum_{i=1}^{p}d[i]-\sum_{i=1}^{p}d[i]\times i ∑i=1p∑j=1id[j]=∑i=1pd[i]×(p−i+1)=(p+1)×∑i=1pd[i]−∑i=1pd[i]×i
-
那么我们可以用树状数组维护两个数组的前缀和:一个数组是 s u m 1 [ i ] = d [ i ] sum1[i]=d[i] sum1[i]=d[i],另一个数组是 s u m 2 [ i ] = d [ i ] × i sum2[i]=d[i]\times i sum2[i]=d[i]×i
区 间 修 改 区间修改 区间修改
对于 t 1 t1 t1 数组的修改同上对 d d d 数组的修改。
对于 t 2 t2 t2 数组的修改也类似,我们给 t 2 [ l ] t2[l] t2[l] 加上 l × x l \times x l×x,给 t 2 [ r + 1 ] t2[r + 1] t2[r+1] 减去 ( r + 1 ) × x (r + 1) \times x (r+1)×x。
A C c o d e AC code ACcode
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+5;
int n,q,a[maxn],maxx,b[maxn],c[maxn];
inline int lowbit(int a)
{
return a&(-a);
}
inline void update(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
{
b[i]+=y;
c[i]+=x*y;
}
}
inline void range_update(int l,int r,int x)
{
update(l,x);
update(r+1,-x);
}
inline int query(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
{
ans += (x+1) * b[i] - c[i];
}
return ans;
}
inline int range_query(int l,int r)
{
return query(r)-query(l-1);
}
signed main()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
update(i,a[i]-a[i-1]);
}
for(int i=1;i<=q;i++)
{
int abc,x,y,z;
cin>>abc;
if(abc==1)
{
cin>>x>>y>>z;
range_update(x,y,z);
}
else
{
cin>>x>>y;
cout<<range_query(x,y)<<endl;
}
}
return 0;
}