众所周知树状数组是很强大的
目前已知可以维护前缀和,不修改的前缀最值,前缀位运算等等
反正总之就是一个巨强的前缀数据结构
如果需要区间修改单点查询,可以差分解决
那么区间修改区间查询呢。
举个例子:
- a[1]~a[n]的数组,每次操作要么给a[l]~a[r]每个位置+x,要么查询a[l]~a[r]的和,操作数105
同样考虑差分,记
c
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
c[i]=a[i]-a[i-1]
c[i]=a[i]−a[i−1],那么
∑
i
=
1
n
c
[
i
]
=
a
[
n
]
\sum_{i=1}^nc[i]=a[n]
i=1∑nc[i]=a[n]
区间修改a[l]~a[r]相当于给c[l]+x,c[r+1]-x
要区间查询,那么记
b
[
i
]
=
∑
j
=
1
i
a
[
j
]
b[i]=\sum_{j=1}^{i}a[j]
b[i]=∑j=1ia[j],要求的相当于就是b的单点查询
b
[
n
]
=
∑
i
=
1
n
a
[
i
]
=
∑
i
=
1
n
∑
j
=
1
i
c
[
i
]
=
∑
i
=
1
n
c
[
i
]
∗
(
n
−
i
+
1
)
=
(
∑
i
=
1
n
c
[
i
]
)
∗
(
n
+
1
)
−
(
∑
i
=
1
n
c
[
i
]
∗
i
)
b[n]=\sum_{i=1}^na[i]=\sum_{i=1}^n\sum_{j=1}^ic[i]\\ =\sum_{i=1}^nc[i]*(n-i+1)\\ =\left(\sum_{i=1}^nc[i]\right)*(n+1)-\left(\sum_{i=1}^nc[i]*i\right)
b[n]=i=1∑na[i]=i=1∑nj=1∑ic[i]=i=1∑nc[i]∗(n−i+1)=(i=1∑nc[i])∗(n+1)−(i=1∑nc[i]∗i)
只需要两个树状数组,分别维护c[i]的前缀和,c[i]*i的前缀和即可
LL arr[2][maxn];
inline void upd(int x,int d)//修改c[x]
{
for(int i=x;i<=n;i+=i&-i)
arr[0][i]+=d,arr[1][i]+=1ll*d*x;//分别维护c[x],c[x]*x的前缀和
}
inline LL qsum(int x)
{
LL s[2]={0};
for(int i=x;i;i-=i&-i) s[0]+=arr[0][i],s[1]+=arr[1][i];
return s[0]*(x+1)-s[1];
}
inline void modify(int l,int r,int d)
{
upd(l,d),upd(r+1,-d);
}
inline LL query(int l,int r)
{
return qsum(r)-qsum(l-1);
}
区间修改实际上就是差分再差分,最后变成一个单点问题,就可以用树状数组了,拓展到矩形求和,或者更高维也是可以的。