1 介绍
本专题用来记录树状数组相关题目。
lowbit(x)
操作,求数 x
二进制表示中最低位的1的值,
int lowbit(int x) {
return x & -x;
}
树状数组:用来快速计算动态前缀和的数据结构。
c[x]
的表示原数组以第x
个数结尾,且区间长度为lowbit(x)
的区间的和,即c[x - lowbit(x) + 1] ~ c[x]
。
树状数组支持两种操作:
- 单点修改,比如
a[i] += x
- 区间查询,比如求
a[l~r]
的和
见上图,修改a[5]
的值,则需要递次修改c[5], c[6], c[8]
的值。那么修改操作的代码如下,
//a[x] += k
void add(int x, int k) {
while (x <= n) {
// 不能越界。n就是最大值,总共n个数,编号分别为1,2,3...,n
c[x] = c[x] + k;
x = x + lowbit(x);
}
}
//或可以写成
void add(int x, int k) {
for (int i = x; i <= n; i += lowbit(i)) {
c[i] += k;
}
}
见上图,查询前缀和a[1~5]
,则需要递次遍历c[5], c[4], c[0]
,将它们的值累加就是前缀和。那么求前缀和的代码如下,
int getsum(int x) {
// a[1]..a[x]的和
int ans = 0;
while (x >= 1) {
ans = ans + c[x];
x = x - lowbit(x);
}
return ans;
}
//或可以写成
int getsum(int x) {
int ans = 0;
for (int i = x; i >= 1; i -= lowbit(i)) {
ans += c[i];
}
return ans;
}
2 训练
题目1:241楼兰图腾
C++代码如下,
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 200010;
int n;
int a[N]; //a[x] = 2表示数x出现了2次
int tr[N];
int Greater[N], lower[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x) {
int res = 0;
for (int i