树状数组
- 一个数字与它的相反数与,结果是二进制下这个数最靠右的1的位置,操作称作lowbit。
inline int lowbit(int x)
{
return x & -x;
}
- 树状数组的存放规则:第k位存放从k往前数lowbit(k)个数的和
单点修改,区间查询
单点修改:不断+自己的lowbit,直到n为止
void update(int x, int k)
{
while (x <= n)
{
tree[x] += k;
x += lowbit(x);
}
}
区间查询:
[
1
,
x
]
[1,x]
[1,x] 区间和,
x
x
x 不断减去自己的lowbit,直到
0
0
0 ,每次将tree[x]加入答案中。利用前缀和就可以求出任意区间的和。
int query(int x)
{
int sum = 0;
while (x > 0)
{
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
区间修改
-
设差分数组
d
i
d_i
di? ,则每个点的值
a
i
=
∑
i
=
1
n
d
i
a_i=\sum_{i=1}^{n}d_i
ai?=∑i=1n?di? 。 -
区间修改
[
l
,
r
]
[l,r]
[l,r] ,每个元素
+
k
+k
+k ,即
d
l
←
d
l
+
k
,
d
r
+
1
←
d
r
+
1
?
k
d_l\leftarrow d_l+k,d_{r+1}\leftarrow d_{r+1}-k
dl?←dl?+k,dr+1?←dr+1??k ,将差分数组建成树状数组,则区间修改对应修改单点值。 -
单点查询:求
[
1
,
x
]
[1,x]
[1,x] 区间和即可
for (int i = 1; i <= n; i++)
{
cin >> val;
update(i, val);
update(i + 1, -val);
}
update(l, k);
update(r+1, -k);
cout << query(x) << endl;
for (int i = 1; i <= n; i++)
cin >> a[i];
cout << a[x] + query(x) << endl;
- 区间查询:
∑
i
=
1
n
a
i
=
∑
i
=
1
n
∑
j
=
1
i
d
j
=
∑
i
=
1
n
(
n
?
i
+
1
)
d
i
=
(
n
+
1
)
∑
i
=
1
n
d
i
?
∑
i
=
1
n
i
d
i
\sum_{i=1}^{n}a_i=\sum_{i=1}^{n}\sum_{j=1}^{i}d_j=\sum_{i=1}^{n}(n-i+1)d_i=(n+1)\sum_{i=1}^{n}d_i-\sum_{i=1}^{n}id_i
∑i=1n?ai?=∑i=1n?∑j=1i?dj?=∑i=1n?(n?i+1)di?=(n+1)∑i=1n?di??∑i=1n?idi? ,维护两个树状数组,
d
i
d_i
di? 和
i
d
i
id_i
idi? ,修改相应的update参数就可以了。
T1.update(l, k);
T1.update(r + 1, -k);
T2.update(l, l * k);
T2.update(r + 1, (r + 1) * (-k));
inline int query(int x)
{
return (x + 1) * T1.query(x) - T2.query(x);
}
|