树状数组入门与基础应用
介绍
树状数组或者二叉索引树也称作BIT;它的查询和修改的时间复杂度都是O(log(n)),空间复杂度则为O(n),这是因为树状数组通过普通数组线性结构转化成树状结构,从而进行跳跃式扫描。通常使用在高效的计算数组的前缀和,区间和。
先通过这个例子引出树状数组的概念。
对长度为n的数列{a1,a2,a3,…an},进行如下操作。
(1)修改元素函数add( x , d ):把ax加上d
(2)求和函数sum( x ):即求前x项数的和,区间和[ l , r ]便为sum( r ) - sum( l - 1 )。
#define lowbit(x) ((x) & -(x))
void add(int x, int d)//修改树中和ax有关的tree[]
{
while (x <= n)
{
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x)//求和
{
int sum = 0;
while (x)
{
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
c[1] = a[1];//1
c[2] = a[1] + a[2];//10
c[3] = a[3];//11
c[4] = a[1] + a[2] + a[3] + a[4];//100
c[5] = a[5];//101
c[6] = a[5] + a[6];//110
c[7] = a[7];//111
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8];//1000
可以发现,这颗树是有规律的.
c[i] = a[i - 2k+1] + a[i - 2k+2] + … + a[i]; //k为i的二进制中从最低位到高位连续0的长度。
而2^k = i&(-i) 即为lowbit(x)所求。
模板与基础应用
如题,已知一个数列,你需要进行下面两种操作:
1.将某一个数加上 xx
2.求出某区间每一个数的和
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x) & -(x))
const int N = 500010;
int tree[N];
int n, m;
void add(int x, int d)
{
while (x <= n)
{
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x)
{
int sum = 0;
while (x)
{
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
int main()
{
ios::sync_with_stdio(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
add(i, x);
}
for (int i = 1; i <= m; i++)
{
int t, x, y;
cin >> t >> x >> y;
if (t == 1)
add(x, y);
else
cout << sum(y) - sum(x - 1) << endl;
}
return 0;
}
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数数加上 xx;
2.求出某一个数的值。
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x) & -(x))
const int N = 500005;
int tree[N], b[N];
int n, m, tem;
void add(int x, int d)
{
while (x <= n)
{
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x)
{
int sum = 0;
while (x)
{
sum += tree[x];
x -= lowbit(x);
}
return sum;
}
int main()
{
ios::sync_with_stdio(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
if (i == 1)
b[i] = x;
else
b[i] = x - tem;
tem = x;
add(i, b[i]);
}//这里建立差分数组
for (int i = 1; i <= m; i++)
{
int t, x, y, d;
cin >> t;
if (t == 1)
{
cin >> x >> y >> d;
add(x, d);
add(y + 1, -d);
}//由于区间修改差分数组只改变x,y+1节点的数值
else
{
cin >> x;
cout << sum(x) << endl;
}//差分数组前x项和便为第x项数值
}
return 0;
}
另树状数组也常用离散化的方法求逆序对的个数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const int maxn = 99999997;
#define lowbit(x) ((x) & -(x))
struct node
{
int num;
int pos;
} a[N], b[N];
int c[N], n, tree[N];
void add(int x, int d)
{
while (x <= n)
{
tree[x] += d;
tree[x] %= maxn;
x += lowbit(x);
}
}
int sum(int x)
{
int sum = 0;
while (x)
{
sum += tree[x];
sum %= maxn;
x -= lowbit(x);
}
return sum;
}
bool cmp(node a,node b)
{
return a.num < b.num;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n;i++)
{
scanf("%d", &a[i].num);
a[i].pos = i;
}
for (int i = 1; i <= n;i++)
{
scanf("%d", &b[i].num);
b[i].pos = i;
}
sort(a + 1, a + 1 + n, cmp);
sort(b + 1, b + 1 + n, cmp);//此离散化手法为用数排数列的第几大代表数
for (int i = 1; i <= n;i++)
c[a[i].pos] = b[i].pos;//离散化 c[i]=i为此题要达到的目标即求逆序对
int ans = 0;
for (int i = 1; i <= n;i++)
{
add(c[i], 1);
ans += i - sum(c[i]);//由于树节点的加入有先后,所以sum(x)是节点左侧比x小的数的多少
ans %= maxn;
}
printf("%d\n", ans);
return 0;
}
本文章部分数据说明来自树状数组详解