前言
这篇博客算是对树状数组应用的总结,并不是树状数组的讲解。
单点修改-区间查询
这就是最普通的树状数组,区间查询我们查询前缀和之后减掉就能得解。
void add(int x,int data){
for (int i=x;i<=n;i+=i&-i) T[i]+=data;
}
int query(int x){
for (int i=x;i;i-=i&-i) ret+=T[x];
return ret;
}
区间修改-单点查询
我们运用差分的思想,我们把原先T表示原数组改为表示差分数组,这样我们单点查询就变成了查询前缀和,而区间修改就变成了两个端点上的单点修改了,那么就和第一个操作一样了。
区间修改-区间查询
这个功能非常的强大,而且用树状数组实现相较与线段树常数很小,所以非常好用,而且代码非常的简短。
我们还是利用差分的思想,我们用delta[i]表示区间i..n加上delta[i]的值,那么我们的前缀和就可以表示为
sum[i]=a[1]+a[2]+...+a[i]+delta[1]∗i+delta[2]∗(i−1)+...+delta[i]∗1
s
u
m
[
i
]
=
a
[
1
]
+
a
[
2
]
+
.
.
.
+
a
[
i
]
+
d
e
l
t
a
[
1
]
∗
i
+
d
e
l
t
a
[
2
]
∗
(
i
−
1
)
+
.
.
.
+
d
e
l
t
a
[
i
]
∗
1
化简一下
sum[i]=sigma(a[x])+sigma(delta[x]∗(i+1−x))(1<=x<=i)
s
u
m
[
i
]
=
s
i
g
m
a
(
a
[
x
]
)
+
s
i
g
m
a
(
d
e
l
t
a
[
x
]
∗
(
i
+
1
−
x
)
)
(
1
<=
x
<=
i
)
re化简一波
sum[i]=sigma(a[x])+(i+1)∗sigma(delta[x])−sigma(delta[x]∗x)(1<=x<=i)
s
u
m
[
i
]
=
s
i
g
m
a
(
a
[
x
]
)
+
(
i
+
1
)
∗
s
i
g
m
a
(
d
e
l
t
a
[
x
]
)
−
s
i
g
m
a
(
d
e
l
t
a
[
x
]
∗
x
)
(
1
<=
x
<=
i
)
我们发现
sigma(a[x])
s
i
g
m
a
(
a
[
x
]
)
很好处理,直接前缀和一波
sigma(delta[x])
s
i
g
m
a
(
d
e
l
t
a
[
x
]
)
和
sigma(delta[x]∗x)
s
i
g
m
a
(
d
e
l
t
a
[
x
]
∗
x
)
都可以用树状数组维护区间修改单点查询完成,那么我们就解决了这个问题
inline void add(int x,int y){
LL add1=y,add2=x*y;
for (int i=x;i<=n;i+=i&-i)
T1[i]+=add1,T2[i]+=add2;
}
inline LL query(LL x){
LL sum1=0,sum2=0;
for (int i=x;i>0;i-=i&-i){
sum1+=T1[i],sum2+=T2[i];
}
return sum1*(x+1)-sum2;
}