树状数组
一维树状数组
假设c[] 为树状数组,a[]为原数组,它们的关系是,c[i]代表从a[i]开始往前2^k个元素的和。(k为i转化成二进制后尾部包含0的个数)。数组的下标从1开始。
即 c[1] =a[1];
c[2] = a[1] +a[2];
c[3] = a[3];
c[4] = a[1] +a[2] + a[3] + a[4];
c[5] = a[5];
c[6] = a[5] +a[6];
c[7] = a[7];
c[8] = a[1] +a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8];
数据间的相互关系构成了一颗树。每一个数都用二进制表示。
通过位运算求2^k = i&(-i)。一系列的操作都是在O(1),因为一个数的二进制位数是一定的。比如 (x += x &(-x))&& x < N,每循环一下,x的最低二进制高向至少上移了一位 8 -> 16 ->32,从上图的树状数组的结构图也可以看出。
int c[N];
int lowbit(int k)
{
return k&(-k);
}
void modify(int x,int value)
{
for (int i =x; i < N; i += lowbit(i))
c[i] +=value;
}
int sum(int x)
{
int s = 0;
for (int i =x; i >= 1; i -= lowbit(i))
s += c[i];
return s;
}
树状数组可以快速的统计出一段连续区间的值,线段树也可以做到,一般树状数组会稍快一些。i处的值为 sum(i)– sum(i-1)。
按照这个原理,可以很方便的推广到二维树状数组。
二维树状数组
二维树状数组的思想几乎一样,也可以推广到三维、四维。
int dp[N][N];
int n,m;
int LowBit(int k)
{
return k& (-k);
}
void modify(int x,int y,int value)
{
for (int i =x; i <= n; i += LowBit(i))
for (intj = y; j <= m; j += LowBit(j))
dp[i][j] += value;
}
int sum(int x,int y)
{
int s = 0;
for (int i =x; i >= 1; i -= LowBit(i))
for (intj = y; j >= 1; j -= LowBit(j))
s +=dp[i][j];
return s;
}
二维树状数组在i,j处的值为sum(i,j)+sum(i-1,j-1)-sum(i-1,j)-sun(i,j-1).