特点:lowbit(x)=最低含 1 位的二进制权值 =tree[x]的维护的长度
tree[x]保存以x为根的子树中叶节点值的和
tree[x]的父节点为tree[x+lowbit(x)]
点单修改&区间查询
add操作,即单点修改:
query操作,即查询前缀和:
ask操作,即查询区间和:
两个前缀和相减。
代码:
#include<iostream>
#define lowbit(x) (x & -x) // lowbit运算
using namespace std;
int tree[25],a[25], n;
//在第idx位置加上k
void add(int idx, int k) {
for (; idx <= n; idx += lowbit(idx)) {
tree[idx] += k;
}
}
//查询第idx位置的前缀和
int query(int idx) {
int ans = 0;
for (; idx >= 1; idx -= lowbit(idx)) {
ans += tree[idx];
}
return ans;
}
//查询区间[l, r]的和
int ask(int l, int r) {
return query(r) - query(l - 1);
}
int main(){
for (int i = 1; i <= n; i++) {
cin >> a[i];
add(i, a[i]);
}
return 0;
}
区间修改&单点查询
用树状数组tree[]维护一个增量数组(利用了差分数组的原理),当要在[x, y]区间加上k时,只需要在差分数组x处加k,在y处减k,则query(x)得到是原数组a[]在x处的增量(差分数组在x处的前缀和为数组在x处元素值——>增量数组在x处的前缀和为数组在x处的增量)。
注意:最后单点查询输出的是原数组元素值a[x]加上增量值tree[x](a[x] + tree[x])。
代码:
#include<iostream>
#define lowbit(x) (x & -x) // lowbit运算
using namespace std;
int tree[25],a[25], n, q;
//在第idx位置加上k
void add(int idx, int k) {
for (; idx <= n; idx += lowbit(idx)) {
tree[idx] += k;
}
}
//查询第idx位置的前缀和
int query(int idx) {
int ans = 0;
for (; idx >= 1; idx -= lowbit(idx)) {
ans += tree[idx];
}
return ans;
}
int main(){
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= q; i++) {
int op, x, y, k;
cin >> op;
if (op == 1) {
cin >> x >> y >> k;
add(x, k);
add(y + 1, -k);
}
if (op == 2) {
cin >> x;
cout << a[x] + query(x) << endl;
}
}
return 0;
}
1、树状数组处理逆序对问题
树状数据相当于桶,数列中每出现一个数其对应位置的桶内元素+1,则该树状数组的前缀和query(x)为x和比x小的数在数列中的出现次数。我们逐一将数列中的数放进树状数组这个桶里,当处理到数列中第i个数时,其对应的逆序对个数为i - query(i)。
逆序对——>统计比x大的数的个数——>排序后用桶来统计——>要统计的个数对应的桶是连续的——>区间和——>树状数组。
2、逆序对在排序中的应用
P1966 [NOIP2013 提高组] 火柴排队 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
将数组A和数组B中元素相同的位置排序相同
方法:c[B[i].x]=A[i].x
A:2 3 1 4->1 2 3 4对应原编号为:3 1 2 4
B:3 2 1 4->1 2 3 4对应原编号为:3 2 1 4
c[B[1]编号]=c[3]=a[1]编号=3
c[B[2]编号]=c[2]=a[2]编号=1
c[B[3]编号]=c[1]=a[3]编号=2
c[B[4]编号]=c[4]=a[4]编号=4
于是c[1]=2 c[2]=1 c[3]=3 c[4]=4
逆序对数=1,交换一次,结束;
为什么上述操作可以实现?因为产生了逆序;只要序列原来对应的数是符合要求的,他们编号相同,那么我们排完序两数的相对位置不发生改变,因此不会产生逆序;一旦A中编号与B中的不同,c数组中就会产生逆序对。
在冒泡排序中,数列中逆序对个数为冒泡排序中相邻两数交换总次数。
3、逆序对问题